[31/34] Windows gdb+gdbserver: Check whether DBG_REPLY_LATER is available

Message ID 20240507234233.371123-32-pedro@palves.net
State New
Headers
Series Windows non-stop mode |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Pedro Alves May 7, 2024, 11:42 p.m. UTC
  Per
<https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-continuedebugevent>,
DBG_REPLY_LATER is "Supported in Windows 10, version 1507 or above, ..."

On Linux, we check which ptrace options are supported by the running
kernel by forking gdb and then the parent gdb debugging the child gdb
with PTRACE_ME, and then trying to set the ptrace options.

Doing something like that on Windows would be more complicated,
because #1 - we can't just fork, we have to start some executable, and
the only executable we know we can start, probably, is gdb itself.
And that's a large program, so take time to be started.  And then we'd
have to implement a WaitForDebugEvent loop to start up the process,
and then finally try ContinueDebugEvent(DBG_REPLY_LATER).  It seems a
lot simpler to just check the Windows version.  Unlike on Linux, we
don't have to worry about kernel feature backports.

Windows has a number of functions you can use to check the OS version,
like GetVersion/GetVersionEx, or the Version Helper functions like
IsWindows10OrGreater, VerifyVersionInfo, etc., however, as explained by

  https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa

... and other pages, "Applications not manifested for Windows 8.1 or
Windows 10 will return the Windows 8 OS version value (6.2)."

"Manifested" here means that the application is linked with an xml
manifest as detailed at:
https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1

I have actually tried doing that with windres, but I wasn't able to
make it work.

However, I found out that we can find the Windows major/minor/build in
the KUSER_SHARED_DATA structure, which defines the layout of a data
area that the kernel places at a pre-set address for sharing with
user-mode software:

  https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kuser_shared_data/index.htm

The Windows major/minor/build version retrieved using that method
bypasses the manifest stuff, it actually gets you the real OS version
numbers.  That is what this patch is using.

Change-Id: Ia27b981aeecaeef430ec90cebc5b3abdce00449d
---
 gdb/nat/windows-nat.c | 60 +++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/windows-nat.h |  4 +++
 2 files changed, 64 insertions(+)
  

Comments

Eli Zaretskii May 8, 2024, 12:45 p.m. UTC | #1
> From: Pedro Alves <pedro@palves.net>
> Date: Wed,  8 May 2024 00:42:30 +0100
> 
> Windows has a number of functions you can use to check the OS version,
> like GetVersion/GetVersionEx, or the Version Helper functions like
> IsWindows10OrGreater, VerifyVersionInfo, etc., however, as explained by
> 
>   https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
> 
> ... and other pages, "Applications not manifested for Windows 8.1 or
> Windows 10 will return the Windows 8 OS version value (6.2)."
> 
> "Manifested" here means that the application is linked with an xml
> manifest as detailed at:
> https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
> 
> I have actually tried doing that with windres, but I wasn't able to
> make it work.

It should be easy to do that by providing a separate manifest file as
part of the tarball, and installing it alongside gdb.exe as part of
"make install".  Emacs does that, for example, and thus can make use
of the accurate Windows version where it needs that using
GetVersion/GetVersionEx.  On Windows 11, for example, I get this in
Emacs:

  M-: (w32-version) RET
   => (10 0 22631)

However, ...

> However, I found out that we can find the Windows major/minor/build in
> the KUSER_SHARED_DATA structure, which defines the layout of a data
> area that the kernel places at a pre-set address for sharing with
> user-mode software:
> 
>   https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kuser_shared_data/index.htm
> 
> The Windows major/minor/build version retrieved using that method
> bypasses the manifest stuff, it actually gets you the real OS version
> numbers.  That is what this patch is using.

...do we really need to do this via a version-check?  Can't we instead
just call ContinueDebugEvent and if it fails, consider DBG_REPLY_LATER
unsupported?  (If calling ContinueDebugEvent with that flag on older
versions of Windows causes an exception, we could use try/catch.)  If
this works, it is a more reliable way to test, IMO and IME.  I think
we should prefer that to poking kernel data structures.

WDYT?
  

Patch

diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 4fd717e6521..99bdadd80e6 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -939,6 +939,66 @@  disable_randomization_available ()
 	  && DeleteProcThreadAttributeList != nullptr);
 }
 
+/* We can find the Windows major/minor/build in the KUSER_SHARED_DATA
+   structure, which defines the layout of a data area that the kernel
+   places at a pre-set address for sharing with user-mode software.
+
+     https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kuser_shared_data/index.htm
+
+   GetVersionEx or the Version Helper functions (IsWindows10OrGreater,
+   VerifyVersionInfo, etc.) don't work for Windows 10 and above,
+   because unless the binary includes a XML manifest declaring support
+   for such Windows versions, the functions always return "Windows
+   8.2".  KUSER_SHARED_DATA gives you the actual running OS info even
+   without the manifest.  */
+#define KUSER_SHARED_DATA 0x7ffe0000
+
+/* The Windows major version number.  */
+
+static inline ULONG
+windows_major_version ()
+{
+  return *(ULONG *) (KUSER_SHARED_DATA + 0x026c);
+}
+
+/* The Windows minor version number.  */
+
+static inline ULONG
+windows_minor_version ()
+{
+  return *(ULONG *) (KUSER_SHARED_DATA + 0x0270);
+}
+
+/* The Windows build number.  Windows build version info can be found
+   here:
+
+     https://www.gaijin.at/en/infos/windows-version-numbers
+ */
+
+static inline ULONG
+windows_build_number ()
+{
+  return *(ULONG *) (KUSER_SHARED_DATA + 0x0260);
+}
+
+/* See windows-nat.h.  */
+
+bool
+dbg_reply_later_available ()
+{
+#if 0
+  debug_printf ("Windows Major Version: %d\n", windows_major_version ());
+  debug_printf ("Windows Minor Version: %d\n", windows_minor_version ());
+  debug_printf ("Windows Build Number: %d\n", windows_build_number ());
+#endif
+
+  /* Supported since Windows 10, Version 1507, which is reported as
+     build number 10240.  */
+  return (windows_major_version () > 10
+	  || (windows_major_version () == 10
+	      && windows_build_number () >= 10240));
+}
+
 /* See windows-nat.h.  */
 
 bool
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 2efb54e1ce7..256355f6ad0 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -415,6 +415,10 @@  extern DeleteProcThreadAttributeList_ftype *DeleteProcThreadAttributeList;
 
 extern bool disable_randomization_available ();
 
+/* Return true if it's possible to use DBG_REPLY_LATER with
+   ContinueDebugEvent on this host.  */
+extern bool dbg_reply_later_available ();
+
 /* Load any functions which may not be available in ancient versions
    of Windows.  */