Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH 0/7] x86/Linux Target Description Changes
@ 2024-02-01 15:28 Andrew Burgess
  2024-02-01 15:28 ` [PATCH 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
                   ` (7 more replies)
  0 siblings, 8 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This series started when I ran the test script
gdb.server/connect-with-no-symbol-file.exp on an x86-64/Linux target
(with the unix board file) and noticed that two core files were left
behind.

The test is a gdbserver test (even when the unix board is used), and
the two core files are generated by the inferior as gdbserver detaches
from them.

I tracked the problem down to some issues with how gdbserver creates
its target descriptions.

Instead of just fixing gdbserver I think it would be good to share
more of the target description creation code between GDB and
gdbserver, the issue that gdbserver has is already fixed on the GDB
side.

By patch #5 enough code is shared that the problem mentioned above is
fixed.

Patches #6 and #7 try to share more code between GDB and gdbserver as
I think this is a good thing.

Thanks,
Andrew

---

Andrew Burgess (7):
  gdbserver: convert have_ptrace_getregset to a tribool
  gdb/x86: move reading of cs and ds state into gdb/nat directory
  gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  gdb/gdbserver: share some code relating to target description creation
  gdbserver: update target description creation for x86/linux
  gdb/gdbserver: share x86/linux tdesc caching

 gdb/Makefile.in              |   1 +
 gdb/amd64-linux-tdep.c       |  33 +--
 gdb/amd64-linux-tdep.h       |   6 -
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |  32 +--
 gdb/i386-linux-tdep.h        |  23 --
 gdb/nat/x86-linux-tdesc.c    | 405 +++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    | 115 ++++++++++
 gdb/nat/x86-linux.c          |  47 ++++
 gdb/nat/x86-linux.h          |  48 +++++
 gdb/x86-linux-nat.c          | 123 ++---------
 gdbserver/Makefile.in        |   4 +
 gdbserver/configure.srv      |   4 +
 gdbserver/linux-amd64-ipa.cc |  45 +---
 gdbserver/linux-arm-low.cc   |   6 +-
 gdbserver/linux-i386-ipa.cc  |  25 +--
 gdbserver/linux-low.cc       |   2 +-
 gdbserver/linux-low.h        |   2 +-
 gdbserver/linux-x86-low.cc   | 185 +++++-----------
 gdbserver/linux-x86-tdesc.cc | 141 +-----------
 gdbserver/linux-x86-tdesc.h  |  56 -----
 21 files changed, 737 insertions(+), 570 deletions(-)
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h
 delete mode 100644 gdbserver/linux-x86-tdesc.h


base-commit: 05d1b4b4ad7d74a64cc71c53d621241fc393fcb6
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 1/7] gdbserver: convert have_ptrace_getregset to a tribool
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-02-01 15:28 ` [PATCH 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Convert the have_ptrace_getregset global within gdbserver to a
tribool.  This brings the flag into alignment with the corresponding
flag in GDB.

The gdbserver have_ptrace_getregset variable is already used as a
tribool, it just doesn't have the tribool type.

In a future commit I plan to share more code between GDB and
gdbserver, and having this variable be the same type in both code
bases will make the sharing much easier.

There should be no user visible changes after this commit.
---
 gdbserver/linux-arm-low.cc |  6 +++---
 gdbserver/linux-low.cc     |  2 +-
 gdbserver/linux-low.h      |  2 +-
 gdbserver/linux-x86-low.cc | 10 +++++-----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 396ec88081b..b4f0e071c92 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -1007,9 +1007,9 @@ arm_target::low_arch_setup ()
 
   /* Check if PTRACE_GETREGSET works.  */
   if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
-    have_ptrace_getregset = 1;
+    have_ptrace_getregset = TRIBOOL_TRUE;
   else
-    have_ptrace_getregset = 0;
+    have_ptrace_getregset = TRIBOOL_FALSE;
 }
 
 bool
@@ -1122,7 +1122,7 @@ arm_target::get_regs_info ()
 {
   const struct target_desc *tdesc = current_process ()->tdesc;
 
-  if (have_ptrace_getregset == 1
+  if (have_ptrace_getregset == TRIBOOL_TRUE
       && (is_aarch32_linux_description (tdesc)
 	  || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
     return &regs_info_aarch32;
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 444eebc6bbe..2949619f503 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -135,7 +135,7 @@ typedef struct
 #endif
 
 /* Does the current host support PTRACE_GETREGSET?  */
-int have_ptrace_getregset = -1;
+enum tribool have_ptrace_getregset = TRIBOOL_UNKNOWN;
 
 /* Return TRUE if THREAD is the leader thread of the process.  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index d34d2738238..eaf87527338 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -951,7 +951,7 @@ void thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid);
 
 bool thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len);
 
-extern int have_ptrace_getregset;
+extern enum tribool have_ptrace_getregset;
 
 /* Search for the value with type MATCH in the auxv vector, with entries of
    length WORDSIZE bytes, of process with pid PID.  If found, store the
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 3af0a009052..0a3bd2c8670 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -899,7 +899,7 @@ x86_linux_read_description (void)
       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
 	{
 	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = 0;
+	  have_ptrace_getregset = TRIBOOL_FALSE;
 	  return i386_linux_read_description (X86_XSTATE_X87);
 	}
       else
@@ -918,7 +918,7 @@ x86_linux_read_description (void)
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-  if (have_ptrace_getregset == -1)
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
       struct iovec iov;
@@ -929,10 +929,10 @@ x86_linux_read_description (void)
       /* Check if PTRACE_GETREGSET works.  */
       if (ptrace (PTRACE_GETREGSET, tid,
 		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = 0;
+	have_ptrace_getregset = TRIBOOL_FALSE;
       else
 	{
-	  have_ptrace_getregset = 1;
+	  have_ptrace_getregset = TRIBOOL_TRUE;
 
 	  /* Get XCR0 from XSAVE extended state.  */
 	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
@@ -951,7 +951,7 @@ x86_linux_read_description (void)
     }
 
   /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset
+  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
 		   && (xcr0 & X86_XSTATE_ALL_MASK));
 
   if (xcr0_features)
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
  2024-02-01 15:28 ` [PATCH 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-02-01 15:28 ` [PATCH 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This patch is part of a series that has the aim of making the code
that, for x86, reads the target description for a native process
shared between GDB and gdbserver.

Within GDB part of this process involves reading the cs and ds state
from the 'struct user_regs_struct' using a ptrace call.

This isn't done by gdbserver, which is part of the motivation for this
whole series; the approach gdbserver takes is inferior to the approach
GDB takes.

This commit moves the reading of cs and ds, which is used to figure
out if a thread is 32-bit or 64-bit (or in x32 mode), into the gdb/nat
directory so that the code could be shared with gdbserver, but at this
point I'm not actually using the code in gdbserver, that will come
later.

As such there should be no user visible changes after this commit, GDB
continues to do things as it did before (reading cs/ds), while
gdbserver continues to use its own approach (which doesn't require
reading cs/ds).
---
 gdb/nat/x86-linux.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux.h | 28 +++++++++++++++++++++++++++
 gdb/x86-linux-nat.c | 42 +++++-----------------------------------
 3 files changed, 80 insertions(+), 37 deletions(-)

diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index e61f4d749ba..4242a1baafb 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -20,6 +20,8 @@
 #include "gdbsupport/common-defs.h"
 #include "x86-linux.h"
 #include "x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include <sys/user.h>
 
 /* Per-thread arch-specific data we want to keep.  */
 
@@ -80,3 +82,48 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp)
 {
   x86_linux_update_debug_registers (lwp);
 }
+
+#ifdef __x86_64__
+/* Value of CS segment register:
+     64bit process: 0x33
+     32bit process: 0x23  */
+#define AMD64_LINUX_USER64_CS 0x33
+
+/* Value of DS segment register:
+     LP64 process: 0x0
+     X32 process: 0x2b  */
+#define AMD64_LINUX_X32_DS 0x2b
+#endif
+
+/* See nat/x86-linux.h.  */
+
+x86_linux_arch_size
+x86_linux_ptrace_get_arch_size (int tid)
+{
+#ifdef __x86_64__
+  unsigned long cs;
+  unsigned long ds;
+
+  /* Get CS register.  */
+  errno = 0;
+  cs = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, cs), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get CS register"));
+
+  bool is_64bit = cs == AMD64_LINUX_USER64_CS;
+
+  /* Get DS register.  */
+  errno = 0;
+  ds = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, ds), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get DS register"));
+
+  bool is_x32 = ds == AMD64_LINUX_X32_DS;
+
+  return x86_linux_arch_size (is_64bit, is_x32);
+#else
+  return x86_linux_arch_size (false, false);
+#endif
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 822882173f9..15153ea277e 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -47,4 +47,32 @@ extern void x86_linux_delete_thread (struct arch_lwp_info *arch_lwp);
 
 extern void x86_linux_prepare_to_resume (struct lwp_info *lwp);
 
+/* Return value from x86_linux_ptrace_get_arch_size function.  Indicates if
+   a thread is 32-bit, 64-bit, or x32.  */
+
+struct x86_linux_arch_size
+{
+  explicit x86_linux_arch_size (bool is_64bit, bool is_x32)
+    : m_is_64bit (is_64bit),
+      m_is_x32 (is_x32)
+  {
+    /* Nothing.  */
+  }
+
+  bool is_64bit () const
+  { return m_is_64bit; }
+
+  bool is_x32 () const
+  { return m_is_x32; }
+
+private:
+  bool m_is_64bit = false;
+  bool m_is_x32 = false;
+};
+
+/* Use ptrace calls to figure out if thread TID is 32-bit, 64-bit, or
+   64-bit running in x32 mode.  */
+
+extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
+
 #endif /* NAT_X86_LINUX_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b93ffca38db..b39d05c401f 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -91,18 +91,6 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
   linux_nat_target::post_startup_inferior (ptid);
 }
 
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
 /* Get Linux/x86 target description from running target.  */
 
 const struct target_desc *
@@ -122,31 +110,11 @@ x86_linux_nat_target::read_description ()
   tid = inferior_ptid.pid ();
 
 #ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  is_64bit = arch_size.is_64bit ();
+  is_x32 = arch_size.is_x32 ();
+
 #elif HAVE_PTRACE_GETFPXREGS
   if (have_ptrace_getfpxregs == -1)
     {
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
  2024-02-01 15:28 ` [PATCH 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
  2024-02-01 15:28 ` [PATCH 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-02-01 15:28 ` [PATCH 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series that aims to share more of the x86
target description reading/generation code between GDB and gdbserver.

There are a huge number of similarities between the code in
gdbserver's x86_linux_read_description function and GDB's
x86_linux_nat_target::read_description function, and it is this
similarity that I plan, in a later commit, to share between GDB and
gdbserver.

However, one thing that is different in x86_linux_read_description is
the code inside the '!use_xml' block.  This is the code that handles
the case where gdbserver is not allowed to send an XML target
description back to GDB.  In this case gdbserver uses some predefined,
fixed, target descriptions.

First, it's worth noting that I suspect this code is not tested any
more.  I couldn't find anything in the testsuite that tries to disable
XML target description support.  And the idea of having a single
"fixed" target description really doesn't work well when we think
about all the various x86 extensions that exist.  Part of me would
like to rip out the no-xml support in gdbserver (at least for x86),
and if a GDB connects that doesn't support XML target descriptions,
gdbserver can just give an error and drop the connection.  GDB has
supported XML target descriptions for 16 years now, I think it would
be reasonable for our shipped gdbserver to drop support for the old
way of doing things.

Anyway.... this commit doesn't do that.

What I did notice was that, over time, the '!use_xml' block appears to
have "drifted" within the x86_linux_read_description function; it's
now not the first check we do.  Instead we make some ptrace calls and
return a target description generated based on the result of these
ptrace calls.  Surely it only makes sense to generate variable target
descriptions if we can send these back to GDB?

So in this commit I propose to move the '!use_xml' block earlier in
the x86_linux_read_description function.

The benefit of this is that this leaves the later half of
x86_linux_read_description much more similar to the GDB function
x86_linux_nat_target::read_description and sets us up for potentially
sharing code between GDB and gdbserver in a later commit.
---
 gdbserver/linux-x86-low.cc | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 0a3bd2c8670..ec80bfe905c 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -891,6 +891,22 @@ x86_linux_read_description (void)
 #endif
     }
 
+  /* If we are not allowed to send an XML target description then we need
+     to use the hard-wired target descriptions.  This corresponds to GDB's
+     default machine for x86.
+
+     This check needs to occur before any returns statements that might
+     generate some alternative target descriptions.  */
+  if (!use_xml)
+    {
+#ifdef __x86_64__
+      if (machine == EM_X86_64)
+	return tdesc_amd64_linux_no_xml.get ();
+      else
+#endif
+	return tdesc_i386_linux_no_xml.get ();
+    }
+
 #if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
   if (machine == EM_386 && have_ptrace_getfpxregs == -1)
     {
@@ -907,17 +923,6 @@ x86_linux_read_description (void)
     }
 #endif
 
-  if (!use_xml)
-    {
-      /* Don't use XML.  */
-#ifdef __x86_64__
-      if (machine == EM_X86_64)
-	return tdesc_amd64_linux_no_xml.get ();
-      else
-#endif
-	return tdesc_i386_linux_no_xml.get ();
-    }
-
   if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
                   ` (2 preceding siblings ...)
  2024-02-01 15:28 ` [PATCH 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-02-01 15:28 ` [PATCH 5/7] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Share the definition of I386_LINUX_XSAVE_XCR0_OFFSET between GDB and
gdbserver.

This commit is part of a series that aims to share more of the x86
target description creation code between GDB and gdbserver.  The
I386_LINUX_XSAVE_XCR0_OFFSET #define is used as part of the target
description creation, and I noticed that this constant is defined
separately for GDB and gdbserver.

This commit moves the definition into gdb/nat/x86-linux.h, which
allows the #define to be shared.

There should be no user visible changes after this commit.
---
 gdb/amd64-linux-tdep.c     |  1 +
 gdb/i386-linux-tdep.c      |  1 +
 gdb/i386-linux-tdep.h      | 20 --------------------
 gdb/nat/x86-linux.h        | 20 ++++++++++++++++++++
 gdbserver/linux-x86-low.cc | 22 +---------------------
 5 files changed, 23 insertions(+), 41 deletions(-)

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index fe7f5d4a259..051c3f74eda 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -42,6 +42,7 @@
 #include "arch/amd64.h"
 #include "target-descriptions.h"
 #include "expop.h"
+#include "nat/x86-linux.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 27aea18c436..43cb2961e28 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -40,6 +40,7 @@
 
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 5891747572b..07593c6a8ec 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -58,26 +58,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 /* Return the target description according to XCR0.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */ 
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 15153ea277e..855a8d14f91 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -22,6 +22,26 @@
 
 #include "nat/linux-nat.h"
 
+/* Format of XSAVE extended state is:
+	struct
+	{
+	  fxsave_bytes[0..463]
+	  sw_usable_bytes[464..511]
+	  xstate_hdr_bytes[512..575]
+	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
+	};
+
+  Same memory layout will be used for the coredump NT_X86_XSTATE
+  representing the XSAVE extended state registers.
+
+  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+  extended state mask, which is the same as the extended control register
+  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
+  together with the mask saved in the xstate_hdr_bytes to determine what
+  states the processor/OS supports and what state, used or initialized,
+  the process/thread is in.  */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
 /* Set whether our local mirror of LWP's debug registers has been
    changed since the values were last written to the thread.  Nonzero
    indicates that a change has been made, zero indicates no change.  */
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index ec80bfe905c..7bade8b3cbf 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -27,6 +27,7 @@
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-xstate.h"
 #include "nat/gdb_ptrace.h"
+#include "nat/x86-linux.h"
 
 #ifdef __x86_64__
 #include "nat/amd64-linux-siginfo.h"
@@ -832,27 +833,6 @@ x86_target::low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
 \f
 static int use_xml;
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  avx_bytes[576..831]
-	  future_state etc
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 /* Does the current host support the GETFPXREGS request?  The header
    file may or may not define it, and even if it is defined, the
    kernel will return EIO if it's running on a pre-SSE processor.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 5/7] gdb/gdbserver: share some code relating to target description creation
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
                   ` (3 preceding siblings ...)
  2024-02-01 15:28 ` [PATCH 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-02-01 15:28 ` [PATCH 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series to share more of the x86 target
description creation code between GDB and gdbserver.

Unlike previous commits which were mostly refactoring, this commit is
the first that makes a real change, though that change should mostly
be for gdbserver; I've largely adopted the "GDB" way of doing things
for gdbserver, and this fixes a real gdbserver bug.

On a x86-64 Linux target, running the test:

  gdb.server/connect-with-no-symbol-file.exp

results in two core files being created.  Both of these core files are
from the inferior process, created after gdbserver has detached.

In this test a gdbserver process is started and then, after gdbserver
has started, but before GDB attaches, we either delete the inferior
executable, or change its permissions so it can't be read.  Only after
doing this do we attempt to connect with GDB.

As GDB connects to gdbserver, gdbserver attempts to figure out the
target description so that it can send the description to GDB, this
involves a call to x86_linux_read_description.

In x86_linux_read_description one of the first things we do is try to
figure out if the process is 32-bit or 64-bit.  To do this we look up
the executable via the thread-id, and then attempt to read the
architecture size from the executable.  This isn't going to work if
the executable has been deleted, or is no longer readable.

And so, as we can't read the executable, we default to an i386 target
and use an i386 target description.

A consequence of using an i386 target description is that addresses
are assumed to be 32-bits.  Here's an example session that shows the
problems this causes.  This is run on an x86-64 machine, and the test
binary (xx.x) is a standard 64-bit x86-64 binary:

  shell_1$ gdbserver --once localhost :54321 /tmp/xx.x

  shell_2$ gdb -q
  (gdb) set sysroot
  (gdb) shell chmod 000 /tmp/xx.x
  (gdb) target remote :54321
  Remote debugging using :54321
  warning: /tmp/xx.x: Permission denied.
  0xf7fd3110 in ?? ()
  (gdb) show architecture
  The target architecture is set to "auto" (currently "i386").
  (gdb) p/x $pc
  $1 = 0xf7fd3110
  (gdb) info proc mappings
  process 2412639
  Mapped address spaces:

  	Start Addr   End Addr       Size     Offset  Perms   objfile
  	  0x400000   0x401000     0x1000        0x0  r--p   /tmp/xx.x
  	  0x401000   0x402000     0x1000     0x1000  r-xp   /tmp/xx.x
  	  0x402000   0x403000     0x1000     0x2000  r--p   /tmp/xx.x
  	  0x403000   0x405000     0x2000     0x2000  rw-p   /tmp/xx.x
  	0xf7fcb000 0xf7fcf000     0x4000        0x0  r--p   [vvar]
  	0xf7fcf000 0xf7fd1000     0x2000        0x0  r-xp   [vdso]
  	0xf7fd1000 0xf7fd3000     0x2000        0x0  r--p   /usr/lib64/ld-2.30.so
  	0xf7fd3000 0xf7ff3000    0x20000     0x2000  r-xp   /usr/lib64/ld-2.30.so
  	0xf7ff3000 0xf7ffb000     0x8000    0x22000  r--p   /usr/lib64/ld-2.30.so
  	0xf7ffc000 0xf7ffe000     0x2000    0x2a000  rw-p   /usr/lib64/ld-2.30.so
  	0xf7ffe000 0xf7fff000     0x1000        0x0  rw-p
  	0xfffda000 0xfffff000    0x25000        0x0  rw-p   [stack]
  	0xff600000 0xff601000     0x1000        0x0  r-xp   [vsyscall]
  (gdb) info inferiors
    Num  Description       Connection           Executable
  * 1    process 2412639   1 (remote :54321)
  (gdb) shell cat /proc/2412639/maps
  00400000-00401000 r--p 00000000 fd:03 45907133           /tmp/xx.x
  00401000-00402000 r-xp 00001000 fd:03 45907133           /tmp/xx.x
  00402000-00403000 r--p 00002000 fd:03 45907133           /tmp/xx.x
  00403000-00405000 rw-p 00002000 fd:03 45907133           /tmp/xx.x
  7ffff7fcb000-7ffff7fcf000 r--p 00000000 00:00 0          [vvar]
  7ffff7fcf000-7ffff7fd1000 r-xp 00000000 00:00 0          [vdso]
  7ffff7fd1000-7ffff7fd3000 r--p 00000000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7fd3000-7ffff7ff3000 r-xp 00002000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ff3000-7ffff7ffb000 r--p 00022000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffc000-7ffff7ffe000 rw-p 0002a000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  7ffffffda000-7ffffffff000 rw-p 00000000 00:00 0          [stack]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]
  (gdb)

Notice the difference between the mappings reported via GDB and those
reported directly from the kernel via /proc/PID/maps, the addresses of
every mapping is clamped to 32-bits for GDB, while the kernel reports
real 64-bit addresses.

Notice also that the $pc value is a 32-bit value.  It appears to be
within one of the mappings reported by GDB, but is outside any of the
mappings reported from the kernel.

And this is where the problem arises.  When gdbserver detaches from
the inferior we pass the inferior the address from which it should
resume.  Due to the 32/64 bit confusion we tell the inferior to resume
from the 32-bit $pc value, which is not within any valid mapping, and
so, as soon as the inferior resumes, it segfaults.

If we look at how GDB (not gdbserver) figures out its target
description then we see an interesting difference.  GDB doesn't try to
read the executable.  Instead GDB uses ptrace to query the thread's
state, and uses this to figure out the if the thread is 32 or 64 bit.

If we update gdbserver to do it the "GDB" way then the above problem
is resolved, gdbserver now sees the process as 64-bit, and when we
detach from the inferior we give it the correct 64-bit address, and
the inferior no longer segfaults.

Now, I could just update the gdbserver code, but better, I think, to
share one copy of the code between GDB and gdbserver in gdb/nat/.
That is what this commit does.

The cores of x86_linux_read_description from gdbserver and
x86_linux_nat_target::read_description from GDB are moved into a new
file gdb/nat/x86-linux-tdesc.c and combined into a single function
x86_linux_tdesc_for_tid which is called from each location.

This new function does things the GDB way, the only changes are to
allow for the sharing; we now have a callback function to call the
first time that the xcr0 state is read, this allows for GDB and
gdbserver to perform their own initialisation as needed, and
additionally, the new function takes a pointer for where to cache the
xcr0 value, this isn't needed for this commit, but will be useful in a
later commit where gdbserver will want to read this cached xcr0
value.

Another thing to note about this commit is how the functions
i386_linux_read_description and amd64_linux_read_description are
handled.  For now I've left these function as implemented separately
in GDB and gdbserver.  I've moved the declarations of these functions
into gdb/nat/x86-linux-tdesc.h, but the implementations are left as
separate.

A later commit in this series will make these functions shared too,
but doing this is not trivial, so I've left that for a separate
commit.  Merging the declarations as I've done here ensures that
everyone implements the function to the same API, and once these
functions are shared (in a later commit) we'll want a shared
declaration anyway.
---
 gdb/Makefile.in              |   1 +
 gdb/amd64-linux-tdep.c       |   1 +
 gdb/amd64-linux-tdep.h       |   6 --
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |   1 +
 gdb/i386-linux-tdep.h        |   3 -
 gdb/nat/x86-linux-tdesc.c    | 118 ++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  75 ++++++++++++++++++
 gdb/x86-linux-nat.c          |  91 ++++------------------
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 +
 gdbserver/linux-i386-ipa.cc  |   1 +
 gdbserver/linux-x86-low.cc   | 144 +++++++++++------------------------
 gdbserver/linux-x86-tdesc.cc |   1 +
 gdbserver/linux-x86-tdesc.h  |   7 --
 15 files changed, 261 insertions(+), 195 deletions(-)
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0e0f19c40c9..76bed92d84e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1586,6 +1586,7 @@ HFILES_NO_SRCDIR = \
 	nat/x86-gcc-cpuid.h \
 	nat/x86-linux.h \
 	nat/x86-linux-dregs.h \
+	nat/x86-linux-tdesc.h \
 	python/py-event.h \
 	python/py-events.h \
 	python/py-stopevent.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 051c3f74eda..87cefaa92e9 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -43,6 +43,7 @@
 #include "target-descriptions.h"
 #include "expop.h"
 #include "nat/x86-linux.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h
index 2003dcda78f..0ec49e7fe03 100644
--- a/gdb/amd64-linux-tdep.h
+++ b/gdb/amd64-linux-tdep.h
@@ -43,12 +43,6 @@ extern struct target_desc *tdesc_x32_linux;
 extern struct target_desc *tdesc_x32_avx_linux;
 extern struct target_desc *tdesc_x32_avx_avx512_linux;
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  */
-
-const target_desc *amd64_linux_read_description (uint64_t xcr0_features_bit,
-						 bool is_x32);
-
 /* Enum that defines the syscall identifiers for amd64 linux.
    Used for process record/replay, these will be translated into
    a gdb-canonical set of syscall ids in linux-record.c.  */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 8b98511cef7..4bcc0696027 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -322,7 +322,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o \
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
 		nat/amd64-linux-siginfo.o"
 		;;
 	    sparc)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 43cb2961e28..895573f8b4f 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -41,6 +41,7 @@
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 07593c6a8ec..e8691cd778e 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -55,9 +55,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 					   struct ui_out *uiout,
 					   enum gdb_signal siggnal);
 
-/* Return the target description according to XCR0.  */
-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
new file mode 100644
index 00000000000..78d22f55d67
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -0,0 +1,118 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "nat/x86-linux-tdesc.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#endif
+#include "arch/i386.h"
+
+#include "gdbsupport/common-defs.h"
+#include "nat/x86-linux.h"
+#include "nat/x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <linux/uio.h>
+#include <elf.h>
+#include <sys/user.h>
+#include <sys/user.h>
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage)
+{
+#ifdef __x86_64__
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  bool is_64bit = arch_size.is_64bit ();
+  bool is_x32 = arch_size.is_x32 ();
+
+  if (sizeof (void *) == 4 && is_64bit && !is_x32)
+    error ("%s", error_msg);
+
+#elif HAVE_PTRACE_GETFPXREGS
+  if (have_ptrace_getfpxregs == -1)
+    {
+      elf_fpxregset_t fpxregs;
+
+      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+	{
+	  have_ptrace_getfpxregs = 0;
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
+	}
+    }
+#endif
+
+  if (*have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    {
+      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, tid,
+		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
+	{
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  *xcr0_storage = 0;
+	}
+      else
+	{
+	  *have_ptrace_getregset = TRIBOOL_TRUE;
+
+	  /* Get XCR0 from XSAVE extended state.  */
+	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+				      / sizeof (uint64_t))];
+
+	  xcr0_init_cb (*xcr0_storage);
+	}
+    }
+
+  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
+     PTRACE_GETREGSET is not available then set xcr0_features_bits to
+     zero so that the "no-features" descriptions are returned by the
+     switches below.  */
+  uint64_t xcr0_features_bits;
+  if (*have_ptrace_getregset == TRIBOOL_TRUE)
+    xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+  else
+    xcr0_features_bits = 0;
+
+#ifdef __x86_64__
+  if (is_64bit)
+    {
+      return amd64_linux_read_description (xcr0_features_bits, is_x32);
+    }
+  else
+#endif
+    return i386_linux_read_description (xcr0_features_bits);
+
+  gdb_assert_not_reached ("failed to return tdesc");
+}
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
new file mode 100644
index 00000000000..3727a8bf95e
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -0,0 +1,75 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_X86_LINUX_TDESC_H
+#define NAT_X86_LINUX_TDESC_H
+
+#include "gdbsupport/function-view.h"
+
+struct target_desc;
+
+/* Return the target description for Linux thread TID.
+
+   When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
+   xcr0 is read using ptrace calls and stored into *XCR0_STORAGE.  Then
+   XCR0_INIT_CB is called with the value of *XCR0_STORAGE and
+   *HAVE_PTRACE_GETREGSET is set to TRIBOOL_TRUE.
+
+   If the attempt to read xcr0 using ptrace fails then *XCR0_STORAGE is set
+   to zero and *HAVE_PTRACE_GETREGSET is set to TRIBOOL_FALSE.
+
+   The storage pointed to by XCR0_STORAGE must exist until the program
+   terminates, this storage is used to cache the xcr0 value.  As such
+   XCR0_INIT_CB will only be called once if xcr0 is successfully read using
+   ptrace, or not at all if the ptrace call fails.
+
+   This function returns a target description based on the extracted xcr0
+   value along with other characteristics of the thread identified by TID.
+
+   This function can return nullptr if we encounter a machine configuration
+   for which a target_desc cannot be created.  Ideally this would not be
+   the case, we should be able to create a target description for every
+   possible machine configuration.  See amd64_linux_read_description and
+   i386_linux_read_description for cases when nullptr might be
+   returned.
+
+   ERROR_MSG is using in an error() call if we try to create a target
+   description for a 64-bit process but this is a 32-bit build of GDB.  */
+
+extern const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage);
+
+#ifdef __x86_64__
+
+/* Return the right amd64-linux target descriptions according to
+   XCR0_FEATURES_BIT and IS_X32.  This is implemented separately in both
+   GDB and gdbserver.  */
+
+extern const target_desc *amd64_linux_read_description
+	(uint64_t xcr0_features_bit, bool is_x32);
+
+#endif
+
+/* Return the target description according to XCR0.  This is implemented
+   separately in both GDB and gdbserver.  */
+extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+
+#endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b39d05c401f..872e27e739f 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -42,6 +42,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "nat/linux-ptrace.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* linux_nat_target::low_new_fork implementation.  */
 
@@ -96,90 +97,26 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
 const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
+  static uint64_t xcr0_storage;
 
   if (inferior_ptid == null_ptid)
     return this->beneath ()->read_description ();
 
-  tid = inferior_ptid.pid ();
-
-#ifdef __x86_64__
-
-  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
-  is_64bit = arch_size.is_64bit ();
-  is_x32 = arch_size.is_x32 ();
-
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
-	}
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  m_xsave_layout = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset == TRIBOOL_TRUE)
-    xcr0_features_bits = xcr0 & X86_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      return amd64_linux_read_description (xcr0_features_bits, is_x32);
-#endif
-    }
-  else
-    {
-      const struct target_desc * tdesc
-	= i386_linux_read_description (xcr0_features_bits);
+  int tid = inferior_ptid.pid ();
 
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE_MASK);
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDB");
 
-      return tdesc;
-    }
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [&] (uint64_t xcr0)
+  {
+    this->m_xsave_layout
+      = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+  };
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb,
+				  error_msg, &xcr0_storage);
 }
 \f
 
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 9e861a75088..7a2702d78bf 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -109,6 +109,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -371,6 +372,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 54e4c9812bb..f97b0d6a1d9 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -23,6 +23,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* Defined in auto-generated file amd64-linux.c.  */
 void init_registers_amd64_linux (void);
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 2e4646f8c03..459b8055b5c 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -23,6 +23,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 7bade8b3cbf..9bf369f8a34 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -48,6 +48,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "linux-x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
 static target_desc_up tdesc_amd64_linux_no_xml;
@@ -844,32 +845,20 @@ int have_ptrace_getfpxregs =
 #endif
 ;
 
+/* Cached xcr0 value.  This is initialised the first time
+   x86_linux_read_description is called.  */
+
+static uint64_t xcr0_storage;
+
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
 x86_linux_read_description (void)
 {
-  unsigned int machine;
-  int is_elf64;
-  int xcr0_features;
-  int tid;
-  static uint64_t xcr0;
-  static int xsave_len;
-  struct regset_info *regset;
-
-  tid = lwpid_of (current_thread);
+  int tid = lwpid_of (current_thread);
 
-  is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
-
-  if (sizeof (void *) == 4)
-    {
-      if (is_elf64 > 0)
-       error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
-      else if (machine == EM_X86_64)
-       error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
-    }
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDBserver");
 
   /* If we are not allowed to send an XML target description then we need
      to use the hard-wired target descriptions.  This corresponds to GDB's
@@ -879,99 +868,54 @@ x86_linux_read_description (void)
      generate some alternative target descriptions.  */
   if (!use_xml)
     {
+      x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+      bool is_64bit = arch_size.is_64bit ();
+      bool is_x32 = arch_size.is_x32 ();
+
+      if (sizeof (void *) == 4 && is_64bit && !is_x32)
+	error ("%s", error_msg);
+
 #ifdef __x86_64__
-      if (machine == EM_X86_64)
+      if (is_64bit && !is_x32)
 	return tdesc_amd64_linux_no_xml.get ();
       else
 #endif
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
-  if (machine == EM_386 && have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87);
-	}
-      else
-	have_ptrace_getfpxregs = 1;
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  xsave_len = x86_xsave_length ();
-
-	  /* Use PTRACE_GETREGSET if it is available.  */
-	  for (regset = x86_regsets;
-	       regset->fill_function != NULL; regset++)
-	    if (regset->get_request == PTRACE_GETREGSET)
-	      regset->size = xsave_len;
-	    else if (regset->type != GENERAL_REGS)
-	      regset->size = 0;
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
-		   && (xcr0 & X86_XSTATE_ALL_MASK));
-
-  if (xcr0_features)
-    i387_set_xsave_mask (xcr0, xsave_len);
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [] (uint64_t xcr0)
+  {
+    i387_set_xsave_mask (xcr0, x86_xsave_length ());
+  };
 
-  if (machine == EM_X86_64)
-    {
-#ifdef __x86_64__
-      const target_desc *tdesc = NULL;
+  /* If have_ptrace_getregset is changed to true by calling
+     x86_linux_tdesc_for_tid then we will perform some additional
+     initialisation.  */
+  bool have_ptrace_getregset_is_unknown
+    = have_ptrace_getregset == TRIBOOL_UNKNOWN;
 
-      if (xcr0_features)
-	{
-	  tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
-						!is_elf64);
-	}
+  const target_desc *tdesc
+    = x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb, error_msg,
+			       &xcr0_storage);
 
-      if (tdesc == NULL)
-	tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
-      return tdesc;
-#endif
-    }
-  else
+  if (have_ptrace_getregset_is_unknown
+      && have_ptrace_getregset == TRIBOOL_TRUE)
     {
-      const target_desc *tdesc = NULL;
-
-      if (xcr0_features)
-	  tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
-
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE);
-
-      return tdesc;
+      int xsave_len = x86_xsave_length ();
+
+      /* Use PTRACE_GETREGSET if it is available.  */
+      for (regset_info *regset = x86_regsets;
+	   regset->fill_function != nullptr;
+	   regset++)
+	if (regset->get_request == PTRACE_GETREGSET)
+	  regset->size = xsave_len;
+	else if (regset->type != GENERAL_REGS)
+	  regset->size = 0;
     }
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return tdesc;
 }
 
 /* Update all the target description of all processes; a new GDB
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 626207fc477..9fd64d8574b 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -26,6 +26,7 @@
 #include "arch/amd64.h"
 #endif
 #include "x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* Return the right x86_linux_tdesc index for a given XCR0.  Return
    X86_TDESC_LAST if can't find a match.  */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index f9561b129ae..576aaf5e165 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -46,11 +46,4 @@ int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
 
 const struct target_desc *i386_get_ipa_tdesc (int idx);
 
-#ifdef __x86_64__
-const struct target_desc *amd64_linux_read_description (uint64_t xcr0,
-							bool is_x32);
-#endif
-
-const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 6/7] gdbserver: update target description creation for x86/linux
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
                   ` (4 preceding siblings ...)
  2024-02-01 15:28 ` [PATCH 5/7] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-02-01 15:28 ` [PATCH 7/7] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series which aims to share more of the target
description creation between GDB and gdbserver for x86/Linux.

After some refactoring, the previous commit actually started to share
some code, we added the shared x86_linux_tdesc_for_tid function into
nat/x86-linux-tdesc.c.  However, this function still relies on
amd64_linux_read_description and i386_linux_read_description which are
implemented separately for both gdbserver and GDB.  Given that at
their core, all these functions to is:

  1. take an xcr0 value as input,
  2. mask out some feature bits,
  3. look for a cached pre-generated target description and return it
     if found,
  4. if no cached target description is found then call either
     amd64_create_target_description or
     i386_create_target_description to create a new target
     description, which is then added to the cache.  Return the newly
     created target description.

The inner functions amd64_create_target_description and
i386_create_target_description are already shared between GDB and
gdbserver (in the gdb/arch/ directory), so the only thing that
the *_read_description functions really do is add the caching layer,
and it feels like this really could be shared.

However, we have a small problem.

On the GDB side we create target descriptions using a different set of
cpu features than on the gdbserver side!  This means that for the
exact same target, we might get a different target description when
using native GDB vs using gdbserver.  This surely feels like a
mistake, I would expect to get the same target description on each.

The table below shows the number of possible different target
descriptions that we can create on the GDB side vs on the gdbserver
side for each target type:

        | GDB | gdbserver
  ------|-----|----------
  i386  | 64  | 7
  amd64 | 32  | 7
  x32   | 16  | 7

So in theory, all I want to do is move the GDB version
of *_read_description into the nat/ directory and have gdbserver use
that, then both GDB and gdbserver would be able to create any of the
possible target descriptions.

Unfortunately it's a little more complex than that due to the in
process agent (IPA).

When the IPA is in use, gdbserver sends a target description index to
the IPA, and the IPA uses this to find the correct target description
to use.

** START OF AN ASIDE **

Back in the day I suspect this approach made perfect sense.  However
since this commit:

  commit a8806230241d201f808d856eaae4d44088117b0c
  Date:   Thu Dec 7 17:07:01 2017 +0000

      Initialize target description early in IPA

I think passing the index is now more trouble than its worth.

We used to pass the index, and then use that index to lookup which
target description to instantiate and use.  However, the above commit
fixed an issue where we can't call malloc() within (certain parts of)
the IPA (apparently), so instead we now pre-compute _every_ possible
target description within the IPA.  The index is now only used to
lookup which of the (many) pre-computed target descriptions to use.

It would (I think) have been easier all around if the IPA just
self-inspected, figured out its own xcr0 value, and used that to
create the one target description that is required.  So long as the
xcr0 to target description code is shared (at compile time) with
gdbserver, then we can be sure that the IPA will derive the same
target description as gdbserver, and we would avoid all this index
passing business, which has made this commit so very, very painful.

** END OF AN ASIDE **

Currently then for x86/linux, gdbserver sends a number between 0 and 7
to the IPA, and the IPA uses this to create a target description.

However, I am proposing that gdbserver should now create one of (up
to) 64 different target descriptions for i386, so this 0 to 7 index
isn't going to be good enough any more (amd64 and x32 have slightly
fewer possible target descriptions, but still more than 8, so the
problem is the same).

For a while I wondered if I was going to have to try and find some
backward compatible solution for this mess.  But after seeing how
lightly the IPA is actually documented, I wonder if it is not the case
that there is a tight coupling between a version of gdbserver and a
version of the IPA?  At least I'm hoping so.

In this commit I have thrown out the old IPA target description index
numbering scheme, and switched to a completely new numbering scheme.
Instead of the index that is passed being arbitrary, the index is
instead calculated from the set of cpu features that are present on
the target.  Within the IPA we can then reverse this logic to recreate
the xcr0 value based on the index, and from the xcr0 value we can
create the correct target description.

With the gdbserver to IPA numbering scheme issue resolved I have then
update the gdbserver versions of amd64_linux_read_description and
i386_linux_read_description so that they create target descriptions
using the same set of cpu features as GDB itself.

After this gdbserver should now always come up with the same target
description as GDB does on any x86/Linux target.

This commit does not introduce any new code sharing between GDB and
gdbserver as previous commits in this series does.  Instead this
commit is all about bringing GDB and gdbserver into alignment
functionally so that the next commit can merge the GDB and gdbserver
versions of these functions.
---
 gdbserver/linux-amd64-ipa.cc |  43 +----
 gdbserver/linux-i386-ipa.cc  |  23 +--
 gdbserver/linux-x86-low.cc   |  15 +-
 gdbserver/linux-x86-tdesc.cc | 315 +++++++++++++++++++++++++----------
 gdbserver/linux-x86-tdesc.h  |  49 +++---
 5 files changed, 277 insertions(+), 168 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index f97b0d6a1d9..42de7d2da16 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -169,47 +169,21 @@ supply_static_tracepoint_registers (struct regcache *regcache,
 
 #endif /* HAVE_UST */
 
-#if !defined __ILP32__
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-#endif
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
 
 #if defined __ILP32__
-  switch (idx)
-    {
-    case X86_TDESC_SSE:
-      return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-    case X86_TDESC_AVX:
-      return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-    case X86_TDESC_AVX_AVX512:
-      return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
-    default:
-      break;
-    }
+  bool is_x32 = true;
 #else
-  return amd64_linux_read_description (idx2mask[idx], false);
+  bool is_x32 = false;
 #endif
 
-  internal_error ("unknown ipa tdesc index: %d", idx);
+  return amd64_linux_read_description (xcr0, is_x32);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a
@@ -277,11 +251,10 @@ void
 initialize_low_tracepoint (void)
 {
 #if defined __ILP32__
-  amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+  for (auto i = 0; i < x86_linux_x32_ipa_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
 #else
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    amd64_linux_read_description (idx2mask[i], false);
+  for (auto i = 0; i < x86_linux_amd64_ipa_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
 #endif
 }
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 459b8055b5c..246bcb9813b 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -246,28 +246,15 @@ initialize_fast_tracepoint_trampoline_buffer (void)
     }
 }
 
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
-  return i386_linux_read_description (idx2mask[idx]);
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+
+  return i386_linux_read_description (xcr0);
 }
 
 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
@@ -289,6 +276,6 @@ void
 initialize_low_tracepoint (void)
 {
   initialize_fast_tracepoint_trampoline_buffer ();
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    i386_linux_read_description (idx2mask[i]);
+  for (auto i = 0; i < x86_linux_i386_ipa_tdesc_count (); i++)
+    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
 }
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 9bf369f8a34..6dbefdb2f26 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -2892,14 +2892,17 @@ x86_target::get_ipa_tdesc_idx ()
   struct regcache *regcache = get_thread_regcache (current_thread, 0);
   const struct target_desc *tdesc = regcache->tdesc;
 
+  if (!use_xml)
+    {
+      if (tdesc == tdesc_i386_linux_no_xml.get ()
 #ifdef __x86_64__
-  return amd64_get_ipa_tdesc_idx (tdesc);
-#endif
-
-  if (tdesc == tdesc_i386_linux_no_xml.get ())
-    return X86_TDESC_SSE;
+	  || tdesc == tdesc_amd64_linux_no_xml.get ()
+#endif /* __x86_64__ */
+	  )
+	return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+    }
 
-  return i386_get_ipa_tdesc_idx (tdesc);
+  return x86_linux_xcr0_to_tdesc_idx (xcr0_storage);
 }
 
 /* The linux target ops object.  */
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 9fd64d8574b..87cf368a336 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -28,96 +28,277 @@
 #include "x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
-/* Return the right x86_linux_tdesc index for a given XCR0.  Return
-   X86_TDESC_LAST if can't find a match.  */
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
 
-static enum x86_linux_tdesc
-xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
 {
-  if (xcr0 & X86_XSTATE_PKRU)
-    {
-      if (is_x32)
-	{
-	  /* No x32 MPX and PKU, fall back to avx_avx512.  */
-	  return X86_TDESC_AVX_AVX512;
-	}
-      else
-	return X86_TDESC_AVX_MPX_AVX512_PKU;
-    }
-  else if (xcr0 & X86_XSTATE_AVX512)
-    return X86_TDESC_AVX_AVX512;
-  else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_ipa_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_ipa_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_ipa_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_AVX_MPX;
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
+	idx |= (1 << i);
     }
-  else if (xcr0 & X86_XSTATE_MPX)
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_MPX;
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
     }
-  else if (xcr0 & X86_XSTATE_AVX)
-    return X86_TDESC_AVX;
-  else if (xcr0 & X86_XSTATE_SSE)
-    return X86_TDESC_SSE;
-  else if (xcr0 & X86_XSTATE_X87)
-    return X86_TDESC_MMX;
-  else
-    return X86_TDESC_LAST;
+
+  return xcr0;
 }
 
+#endif /* IN_PROCESS_AGENT */
+
 #if defined __i386__ || !defined IN_PROCESS_AGENT
 
-static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible i386 target descriptions.  */
 
-/* Return the target description according to XCR0.  */
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 i386_linux_read_description (uint64_t xcr0)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
 
-  struct target_desc **tdesc = &i386_tdescs[idx];
+  target_desc **tdesc = &i386_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = i386_create_target_description (xcr0, true, false);
 
       init_target_desc (*tdesc, i386_expedite_regs);
     }
 
-  return *tdesc;;
+  return *tdesc;
 }
 #endif
 
 #ifdef __x86_64__
 
-static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
-static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
 
-  struct target_desc **tdesc = NULL;
+  target_desc **tdesc = nullptr;
 
   if (is_x32)
     tdesc = &x32_tdescs[idx];
   else
     tdesc = &amd64_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
 
@@ -127,39 +308,3 @@ amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 }
 
 #endif
-
-#ifndef IN_PROCESS_AGENT
-
-int
-i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == i386_tdescs[i])
-	return i;
-    }
-
-  /* If none tdesc is found, return the one with minimum features.  */
-  return X86_TDESC_MMX;
-}
-
-#if defined __x86_64__
-int
-amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == amd64_tdescs[i])
-	return i;
-    }
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == x32_tdescs[i])
-	return i;
-    }
-
-  return X86_TDESC_SSE;
-}
-
-#endif
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index 576aaf5e165..70456e4be44 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -21,29 +21,30 @@
 #ifndef GDBSERVER_LINUX_X86_TDESC_H
 #define GDBSERVER_LINUX_X86_TDESC_H
 
-/* Note: since IPA obviously knows what ABI it's running on (i386 vs x86_64
-   vs x32), it's sufficient to pass only the register set here.  This,
-   together with the ABI known at IPA compile time, maps to a tdesc.  */
-
-enum x86_linux_tdesc {
-  X86_TDESC_MMX = 0,
-  X86_TDESC_SSE = 1,
-  X86_TDESC_AVX = 2,
-  X86_TDESC_MPX = 3,
-  X86_TDESC_AVX_MPX = 4,
-  X86_TDESC_AVX_AVX512 = 5,
-  X86_TDESC_AVX_MPX_AVX512_PKU = 6,
-  X86_TDESC_LAST = 7,
-};
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-int i386_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-#if defined __x86_64__ && !defined IN_PROCESS_AGENT
-int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-const struct target_desc *i386_get_ipa_tdesc (int idx);
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_ipa_tdesc_count ();
+extern int x86_linux_x32_ipa_tdesc_count ();
+extern int x86_linux_i386_ipa_tdesc_count ();
+
+
+#endif /* IN_PROCESS_AGENT */
 
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 7/7] gdb/gdbserver: share x86/linux tdesc caching
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
                   ` (5 preceding siblings ...)
  2024-02-01 15:28 ` [PATCH 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
@ 2024-02-01 15:28 ` Andrew Burgess
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
  7 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-02-01 15:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit builds on the previous series of commits to share the
target description caching code between GDB and gdbserver for
x86/Linux targets.

The objective of this commit is to move the four functions (2 each of)
i386_linux_read_description and amd64_linux_read_description into
gdb/nat/x86-linux-tdesc.c and combine them so we have just a single
copy of each.  Then both GDB and gdbserver will link against these
shared functions.

It is worth reading the description of the previous commit to see why
this merging is not as simple as it seems: on the gdbserver side we
actually have two users of these functions, gdbserver itself, and the
in process agent (IPA).

However, the previous commit streamlined the gdbserver code, and so
now it is simple to move the two functions along with all their
support functions from the gdbserver directory into the gdb/nat/
directory, and then GDB is fine to call these functions.

One small curiosity with this patch is the function
x86_linux_post_init_tdesc.  On the gdbserver side the two functions
amd64_linux_read_description and i386_linux_read_description have some
functionality that is not present on the GDB side, that is some
additional configuration that is performed as each target description
is created to setup the expedited registers.

To support this I've added the function x86_linux_post_init_tdesc.
This function is called from the two *_linux_read_description
functions, but is implemented separately for GDB and gdbserver.

This does mean adding back some non-shared code when this whole series
has been about sharing code, but now the only non-shared bit is the
single line that is actually different between GDB and gdbserver, all
the rest, which is identical, is now shared.

I did need to add a new rule to the gdbserver Makefile, this is to
allow the nat/x86-linux-tdesc.c file to be compiled for the IPA.
---
 gdb/amd64-linux-tdep.c       |  31 ----
 gdb/i386-linux-tdep.c        |  30 +---
 gdb/nat/x86-linux-tdesc.c    | 287 +++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  56 ++++++-
 gdbserver/Makefile.in        |   4 +
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 -
 gdbserver/linux-i386-ipa.cc  |   1 -
 gdbserver/linux-x86-low.cc   |   1 -
 gdbserver/linux-x86-tdesc.cc | 285 +---------------------------------
 gdbserver/linux-x86-tdesc.h  |  50 ------
 11 files changed, 355 insertions(+), 393 deletions(-)
 delete mode 100644 gdbserver/linux-x86-tdesc.h

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 87cefaa92e9..d489a54abfc 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1579,37 +1579,6 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
-const target_desc *
-amd64_linux_read_description (uint64_t xcr0_features_bit, bool is_x32)
-{
-  static target_desc *amd64_linux_tdescs \
-    [2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  static target_desc *x32_linux_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/] = {};
-
-  target_desc **tdesc;
-
-  if (is_x32)
-    {
-      tdesc = &x32_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0 ]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-  else
-    {
-      tdesc = &amd64_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_MPX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-
-  if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0_features_bit, is_x32,
-					      true, true);
-
-  return *tdesc;
-}
-
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 895573f8b4f..0ddd8c665d6 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -680,29 +680,12 @@ i386_linux_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
   return i386_linux_core_read_xsave_info (core_bfd, layout) != 0;
 }
 
-/* See i386-linux-tdep.h.  */
+/* See nat/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  if (xcr0 == 0)
-    return NULL;
-
-  static struct target_desc *i386_linux_tdescs \
-    [2/*X87*/][2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  struct target_desc **tdesc;
-
-  tdesc = &i386_linux_tdescs[(xcr0 & X86_XSTATE_X87) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
-
-  if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true, false);
-
-  return *tdesc;
+  /* Nothing.  */
 }
 
 /* Get Linux/x86 target description from core dump.  */
@@ -715,7 +698,10 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
   /* Linux/i386.  */
   x86_xsave_layout layout;
   uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
-  const struct target_desc *tdesc = i386_linux_read_description (xcr0);
+
+  const struct target_desc *tdesc;
+  if (xcr0 != 0)
+    tdesc = i386_linux_read_description (xcr0);
 
   if (tdesc != NULL)
     return tdesc;
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index 78d22f55d67..297d1873680 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -38,6 +38,8 @@
 #include <sys/user.h>
 #include <sys/user.h>
 
+#ifndef IN_PROCESS_AGENT
+
 /* See nat/x86-linux-tdesc.h.  */
 
 const target_desc *
@@ -116,3 +118,288 @@ x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
 
   gdb_assert_not_reached ("failed to return tdesc");
 }
+
+#endif /* !IN_PROCESS_AGENT */
+
+
+
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
+
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_ipa_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_ipa_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_ipa_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
+	idx |= (1 << i);
+    }
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
+    }
+
+  return xcr0;
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+#if defined __i386__ || !defined IN_PROCESS_AGENT
+
+/* A cache of all possible i386 target descriptions.  */
+
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const struct target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
+
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
+
+  target_desc **tdesc = &i386_tdescs[idx];
+
+  if (*tdesc == nullptr)
+    {
+      *tdesc = i386_create_target_description (xcr0, true, false);
+
+      x86_linux_post_init_tdesc (*tdesc, false);
+    }
+
+  return *tdesc;
+}
+#endif
+
+#ifdef __x86_64__
+
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+{
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
+
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
+
+  target_desc **tdesc = nullptr;
+
+  if (is_x32)
+    tdesc = &x32_tdescs[idx];
+  else
+    tdesc = &amd64_tdescs[idx];
+
+  if (*tdesc == nullptr)
+    {
+      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
+
+      x86_linux_post_init_tdesc (*tdesc, true);
+    }
+  return *tdesc;
+}
+
+#endif
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
index 3727a8bf95e..648fe0ed0b2 100644
--- a/gdb/nat/x86-linux-tdesc.h
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -24,6 +24,8 @@
 
 struct target_desc;
 
+#ifndef IN_PROCESS_AGENT
+
 /* Return the target description for Linux thread TID.
 
    When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
@@ -57,19 +59,57 @@ x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
 			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
 			 const char *error_msg, uint64_t *xcr0_storage);
 
+#endif /* !IN_PROCESS_AGENT */
+
 #ifdef __x86_64__
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  This is implemented separately in both
-   GDB and gdbserver.  */
+/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
 
-extern const target_desc *amd64_linux_read_description
-	(uint64_t xcr0_features_bit, bool is_x32);
+extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
+							bool is_x32);
 
-#endif
+#endif /* __x86_64__ */
+
+/* Return the i386 target description corresponding to XCR0.  */
 
-/* Return the target description according to XCR0.  This is implemented
-   separately in both GDB and gdbserver.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
+/* This function is called from amd64_linux_read_description and
+   i386_linux_read_description after a new target description has been
+   created, TDESC is the new target description, IS_64BIT will be true
+   when called from amd64_linux_read_description, otherwise IS_64BIT will
+   be false.  If the *_linux_read_description functions found a cached
+   target description then this function will not be called.
+
+   Both GDB and gdbserver have their own implementations of this
+   function.  */
+
+extern void x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit);
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_ipa_tdesc_count ();
+extern int x86_linux_x32_ipa_tdesc_count ();
+extern int x86_linux_i386_ipa_tdesc_count ();
+
+#endif /* IN_PROCESS_AGENT */
+
 #endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index d12f8746611..c97649378e8 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -517,6 +517,10 @@ gdbsupport/%-ipa.o: ../gdbsupport/%.cc
 	$(IPAGENT_COMPILE) $<
 	$(POSTCOMPILE)
 
+nat/%-ipa.o: ../gdb/nat/%.c
+	$(IPAGENT_COMPILE) $<
+	$(POSTCOMPILE)
+
 %-ipa.o: ../gdb/%.c
 	$(IPAGENT_COMPILE) -x c++ $<
 	$(POSTCOMPILE)
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7a2702d78bf..36a457f21ed 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -116,6 +116,7 @@ case "${gdbserver_host}" in
 			srv_linux_btrace=yes
 			ipa_obj="linux-i386-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/i386-ipa.o"
+			ipa_obj="${ipa_obj} nat/x86-linux-tdesc-ipa.o"
 			;;
   i[34567]86-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
@@ -380,6 +381,7 @@ case "${gdbserver_host}" in
 			srv_linux_btrace=yes
 			ipa_obj="linux-amd64-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/amd64-ipa.o"
+			ipa_obj="${ipa_obj} nat/x86-linux-tdesc-ipa.o"
 			;;
   x86_64-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o"
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 42de7d2da16..afa6aab5bab 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -21,7 +21,6 @@
 #include "server.h"
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux-tdesc.h"
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 246bcb9813b..f6d26f784af 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -21,7 +21,6 @@
 #include "server.h"
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux-tdesc.h"
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 6dbefdb2f26..7134d20dd7f 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -47,7 +47,6 @@
 #include "nat/linux-nat.h"
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 87cf368a336..39d1d51b8c0 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -19,292 +19,19 @@
 
 #include "server.h"
 #include "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#endif
 #include "x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
-/* A structure used to describe a single cpu feature that might, or might
-   not, be checked for when creating a target description for one of i386,
-   amd64, or x32.  */
-
-struct x86_tdesc_feature {
-  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
-  uint64_t feature;
-
-  /* Is this feature checked when creating an i386 target description.  */
-  bool is_i386;
-
-  /* Is this feature checked when creating an amd64 target description.  */
-  bool is_amd64;
-
-  /* Is this feature checked when creating an x32 target description.  */
-  bool is_x32;
-};
-
-/* A constant table that describes all of the cpu features that are
-   checked when building a target description for i386, amd64, or x32.  */
-
-static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
-  /* Feature,           i386,	amd64,	x32.  */
-  { X86_XSTATE_PKRU,	true,	true, 	true },
-  { X86_XSTATE_AVX512,	true,	true, 	true },
-  { X86_XSTATE_AVX,	true,	true, 	true },
-  { X86_XSTATE_MPX,	true,	true, 	false },
-  { X86_XSTATE_SSE,	true,	false, 	false },
-  { X86_XSTATE_X87,	true,	false, 	false }
-};
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an i386 target description.  */
-
-static constexpr uint64_t
-x86_linux_i386_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an amd64 target description.  */
-
-static constexpr uint64_t
-x86_linux_amd64_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an x32 target description.  */
-
-static constexpr uint64_t
-x86_linux_x32_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an i386 target description.  */
-
-static constexpr int
-x86_linux_i386_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an amd64 target description.  */
-
-static constexpr int
-x86_linux_amd64_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an x32 target description.  */
-
-static constexpr int
-x86_linux_x32_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_amd64_ipa_tdesc_count ()
-{
-  return x86_linux_amd64_tdesc_count ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_x32_ipa_tdesc_count ()
-{
-  return x86_linux_x32_tdesc_count ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_i386_ipa_tdesc_count ()
-{
-  return x86_linux_i386_tdesc_count ();
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
-{
-  /* The following table shows which features are checked for when creating
-     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
-     represents the bit order within the generated index number.
-
-     i386  | x87 sse mpx avx avx512 pkru
-     amd64 |         mpx avx avx512 pkru
-     i32   |             avx avx512 pkru
-
-     The features are ordered so that for each mode (i386, amd64, i32) the
-     generated index will form a continuous range.  */
-
-  int idx = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
-	idx |= (1 << i);
-    }
-
-  return idx;
-}
-
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
-{
-  uint64_t xcr0 = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_tdesc_features[i].feature;
-    }
-
-  return xcr0;
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-/* A cache of all possible i386 target descriptions.  */
-
-static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
-
 /* See nat/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
-
-  target_desc **tdesc = &i386_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = i386_create_target_description (xcr0, true, false);
-
-      init_target_desc (*tdesc, i386_expedite_regs);
-    }
-
-  return *tdesc;
-}
-#endif
-
 #ifdef __x86_64__
-
-/* A cache of all possible amd64 target descriptions.  */
-
-static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
-
-/* A cache of all possible x32 target descriptions.  */
-
-static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
-  if (is_x32)
-    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  if (is_64bit)
+    init_target_desc (tdesc, amd64_expedite_regs);
   else
-    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
-
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  if (is_x32)
-    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
-  else
-    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
-
-  target_desc **tdesc = nullptr;
-
-  if (is_x32)
-    tdesc = &x32_tdescs[idx];
-  else
-    tdesc = &amd64_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
-      init_target_desc (*tdesc, amd64_expedite_regs);
-    }
-  return *tdesc;
+#endif
+    init_target_desc (tdesc, i386_expedite_regs);
 }
 
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
deleted file mode 100644
index 70456e4be44..00000000000
--- a/gdbserver/linux-x86-tdesc.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Low level support for x86 (i386 and x86-64), shared between gdbserver
-   and IPA.
-
-   Copyright (C) 2016-2024 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   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 <http://www.gnu.org/licenses/>.  */
-
-#ifndef GDBSERVER_LINUX_X86_TDESC_H
-#define GDBSERVER_LINUX_X86_TDESC_H
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
-
-/* Within the in-process-agent we need to pre-initialise all of the target
-   descriptions, to do this we need to know how many target descriptions
-   there are for each different target type.  These functions return the
-   target description count for the relevant target.  */
-
-extern int x86_linux_amd64_ipa_tdesc_count ();
-extern int x86_linux_x32_ipa_tdesc_count ();
-extern int x86_linux_i386_ipa_tdesc_count ();
-
-
-#endif /* IN_PROCESS_AGENT */
-
-#endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 0/7] x86/Linux Target Description Changes
  2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
                   ` (6 preceding siblings ...)
  2024-02-01 15:28 ` [PATCH 7/7] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
@ 2024-03-05 17:00 ` Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
                     ` (8 more replies)
  7 siblings, 9 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

In v2:

  - Rebase to current upstream/master, no merge conflicts,

  - Retested.

---

Andrew Burgess (7):
  gdbserver: convert have_ptrace_getregset to a tribool
  gdb/x86: move reading of cs and ds state into gdb/nat directory
  gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  gdb/gdbserver: share some code relating to target description creation
  gdbserver: update target description creation for x86/linux
  gdb/gdbserver: share x86/linux tdesc caching

 gdb/Makefile.in              |   1 +
 gdb/amd64-linux-tdep.c       |  33 +--
 gdb/amd64-linux-tdep.h       |   6 -
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |  32 +--
 gdb/i386-linux-tdep.h        |  23 --
 gdb/nat/x86-linux-tdesc.c    | 405 +++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    | 115 ++++++++++
 gdb/nat/x86-linux.c          |  47 ++++
 gdb/nat/x86-linux.h          |  48 +++++
 gdb/x86-linux-nat.c          | 123 ++---------
 gdbserver/Makefile.in        |   4 +
 gdbserver/configure.srv      |   4 +
 gdbserver/linux-amd64-ipa.cc |  45 +---
 gdbserver/linux-arm-low.cc   |   6 +-
 gdbserver/linux-i386-ipa.cc  |  25 +--
 gdbserver/linux-low.cc       |   2 +-
 gdbserver/linux-low.h        |   2 +-
 gdbserver/linux-x86-low.cc   | 185 +++++-----------
 gdbserver/linux-x86-tdesc.cc | 141 +-----------
 gdbserver/linux-x86-tdesc.h  |  56 -----
 21 files changed, 737 insertions(+), 570 deletions(-)
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h
 delete mode 100644 gdbserver/linux-x86-tdesc.h


base-commit: f08311ceb1ba4e19eab7070e676416337455a074
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 1/7] gdbserver: convert have_ptrace_getregset to a tribool
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Convert the have_ptrace_getregset global within gdbserver to a
tribool.  This brings the flag into alignment with the corresponding
flag in GDB.

The gdbserver have_ptrace_getregset variable is already used as a
tribool, it just doesn't have the tribool type.

In a future commit I plan to share more code between GDB and
gdbserver, and having this variable be the same type in both code
bases will make the sharing much easier.

There should be no user visible changes after this commit.
---
 gdbserver/linux-arm-low.cc |  6 +++---
 gdbserver/linux-low.cc     |  2 +-
 gdbserver/linux-low.h      |  2 +-
 gdbserver/linux-x86-low.cc | 10 +++++-----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 396ec88081b..b4f0e071c92 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -1007,9 +1007,9 @@ arm_target::low_arch_setup ()
 
   /* Check if PTRACE_GETREGSET works.  */
   if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
-    have_ptrace_getregset = 1;
+    have_ptrace_getregset = TRIBOOL_TRUE;
   else
-    have_ptrace_getregset = 0;
+    have_ptrace_getregset = TRIBOOL_FALSE;
 }
 
 bool
@@ -1122,7 +1122,7 @@ arm_target::get_regs_info ()
 {
   const struct target_desc *tdesc = current_process ()->tdesc;
 
-  if (have_ptrace_getregset == 1
+  if (have_ptrace_getregset == TRIBOOL_TRUE
       && (is_aarch32_linux_description (tdesc)
 	  || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
     return &regs_info_aarch32;
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 9d5a6242803..dfa26d451b0 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -135,7 +135,7 @@ typedef struct
 #endif
 
 /* Does the current host support PTRACE_GETREGSET?  */
-int have_ptrace_getregset = -1;
+enum tribool have_ptrace_getregset = TRIBOOL_UNKNOWN;
 
 /* Return TRUE if THREAD is the leader thread of the process.  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index d34d2738238..eaf87527338 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -951,7 +951,7 @@ void thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid);
 
 bool thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len);
 
-extern int have_ptrace_getregset;
+extern enum tribool have_ptrace_getregset;
 
 /* Search for the value with type MATCH in the auxv vector, with entries of
    length WORDSIZE bytes, of process with pid PID.  If found, store the
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 3af0a009052..0a3bd2c8670 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -899,7 +899,7 @@ x86_linux_read_description (void)
       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
 	{
 	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = 0;
+	  have_ptrace_getregset = TRIBOOL_FALSE;
 	  return i386_linux_read_description (X86_XSTATE_X87);
 	}
       else
@@ -918,7 +918,7 @@ x86_linux_read_description (void)
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-  if (have_ptrace_getregset == -1)
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
       struct iovec iov;
@@ -929,10 +929,10 @@ x86_linux_read_description (void)
       /* Check if PTRACE_GETREGSET works.  */
       if (ptrace (PTRACE_GETREGSET, tid,
 		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = 0;
+	have_ptrace_getregset = TRIBOOL_FALSE;
       else
 	{
-	  have_ptrace_getregset = 1;
+	  have_ptrace_getregset = TRIBOOL_TRUE;
 
 	  /* Get XCR0 from XSAVE extended state.  */
 	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
@@ -951,7 +951,7 @@ x86_linux_read_description (void)
     }
 
   /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset
+  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
 		   && (xcr0 & X86_XSTATE_ALL_MASK));
 
   if (xcr0_features)
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This patch is part of a series that has the aim of making the code
that, for x86, reads the target description for a native process
shared between GDB and gdbserver.

Within GDB part of this process involves reading the cs and ds state
from the 'struct user_regs_struct' using a ptrace call.

This isn't done by gdbserver, which is part of the motivation for this
whole series; the approach gdbserver takes is inferior to the approach
GDB takes.

This commit moves the reading of cs and ds, which is used to figure
out if a thread is 32-bit or 64-bit (or in x32 mode), into the gdb/nat
directory so that the code could be shared with gdbserver, but at this
point I'm not actually using the code in gdbserver, that will come
later.

As such there should be no user visible changes after this commit, GDB
continues to do things as it did before (reading cs/ds), while
gdbserver continues to use its own approach (which doesn't require
reading cs/ds).
---
 gdb/nat/x86-linux.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux.h | 28 +++++++++++++++++++++++++++
 gdb/x86-linux-nat.c | 42 +++++-----------------------------------
 3 files changed, 80 insertions(+), 37 deletions(-)

diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index e61f4d749ba..4242a1baafb 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -20,6 +20,8 @@
 #include "gdbsupport/common-defs.h"
 #include "x86-linux.h"
 #include "x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include <sys/user.h>
 
 /* Per-thread arch-specific data we want to keep.  */
 
@@ -80,3 +82,48 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp)
 {
   x86_linux_update_debug_registers (lwp);
 }
+
+#ifdef __x86_64__
+/* Value of CS segment register:
+     64bit process: 0x33
+     32bit process: 0x23  */
+#define AMD64_LINUX_USER64_CS 0x33
+
+/* Value of DS segment register:
+     LP64 process: 0x0
+     X32 process: 0x2b  */
+#define AMD64_LINUX_X32_DS 0x2b
+#endif
+
+/* See nat/x86-linux.h.  */
+
+x86_linux_arch_size
+x86_linux_ptrace_get_arch_size (int tid)
+{
+#ifdef __x86_64__
+  unsigned long cs;
+  unsigned long ds;
+
+  /* Get CS register.  */
+  errno = 0;
+  cs = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, cs), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get CS register"));
+
+  bool is_64bit = cs == AMD64_LINUX_USER64_CS;
+
+  /* Get DS register.  */
+  errno = 0;
+  ds = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, ds), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get DS register"));
+
+  bool is_x32 = ds == AMD64_LINUX_X32_DS;
+
+  return x86_linux_arch_size (is_64bit, is_x32);
+#else
+  return x86_linux_arch_size (false, false);
+#endif
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 822882173f9..15153ea277e 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -47,4 +47,32 @@ extern void x86_linux_delete_thread (struct arch_lwp_info *arch_lwp);
 
 extern void x86_linux_prepare_to_resume (struct lwp_info *lwp);
 
+/* Return value from x86_linux_ptrace_get_arch_size function.  Indicates if
+   a thread is 32-bit, 64-bit, or x32.  */
+
+struct x86_linux_arch_size
+{
+  explicit x86_linux_arch_size (bool is_64bit, bool is_x32)
+    : m_is_64bit (is_64bit),
+      m_is_x32 (is_x32)
+  {
+    /* Nothing.  */
+  }
+
+  bool is_64bit () const
+  { return m_is_64bit; }
+
+  bool is_x32 () const
+  { return m_is_x32; }
+
+private:
+  bool m_is_64bit = false;
+  bool m_is_x32 = false;
+};
+
+/* Use ptrace calls to figure out if thread TID is 32-bit, 64-bit, or
+   64-bit running in x32 mode.  */
+
+extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
+
 #endif /* NAT_X86_LINUX_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b93ffca38db..b39d05c401f 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -91,18 +91,6 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
   linux_nat_target::post_startup_inferior (ptid);
 }
 
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
 /* Get Linux/x86 target description from running target.  */
 
 const struct target_desc *
@@ -122,31 +110,11 @@ x86_linux_nat_target::read_description ()
   tid = inferior_ptid.pid ();
 
 #ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  is_64bit = arch_size.is_64bit ();
+  is_x32 = arch_size.is_x32 ();
+
 #elif HAVE_PTRACE_GETFPXREGS
   if (have_ptrace_getfpxregs == -1)
     {
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series that aims to share more of the x86
target description reading/generation code between GDB and gdbserver.

There are a huge number of similarities between the code in
gdbserver's x86_linux_read_description function and GDB's
x86_linux_nat_target::read_description function, and it is this
similarity that I plan, in a later commit, to share between GDB and
gdbserver.

However, one thing that is different in x86_linux_read_description is
the code inside the '!use_xml' block.  This is the code that handles
the case where gdbserver is not allowed to send an XML target
description back to GDB.  In this case gdbserver uses some predefined,
fixed, target descriptions.

First, it's worth noting that I suspect this code is not tested any
more.  I couldn't find anything in the testsuite that tries to disable
XML target description support.  And the idea of having a single
"fixed" target description really doesn't work well when we think
about all the various x86 extensions that exist.  Part of me would
like to rip out the no-xml support in gdbserver (at least for x86),
and if a GDB connects that doesn't support XML target descriptions,
gdbserver can just give an error and drop the connection.  GDB has
supported XML target descriptions for 16 years now, I think it would
be reasonable for our shipped gdbserver to drop support for the old
way of doing things.

Anyway.... this commit doesn't do that.

What I did notice was that, over time, the '!use_xml' block appears to
have "drifted" within the x86_linux_read_description function; it's
now not the first check we do.  Instead we make some ptrace calls and
return a target description generated based on the result of these
ptrace calls.  Surely it only makes sense to generate variable target
descriptions if we can send these back to GDB?

So in this commit I propose to move the '!use_xml' block earlier in
the x86_linux_read_description function.

The benefit of this is that this leaves the later half of
x86_linux_read_description much more similar to the GDB function
x86_linux_nat_target::read_description and sets us up for potentially
sharing code between GDB and gdbserver in a later commit.
---
 gdbserver/linux-x86-low.cc | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 0a3bd2c8670..ec80bfe905c 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -891,6 +891,22 @@ x86_linux_read_description (void)
 #endif
     }
 
+  /* If we are not allowed to send an XML target description then we need
+     to use the hard-wired target descriptions.  This corresponds to GDB's
+     default machine for x86.
+
+     This check needs to occur before any returns statements that might
+     generate some alternative target descriptions.  */
+  if (!use_xml)
+    {
+#ifdef __x86_64__
+      if (machine == EM_X86_64)
+	return tdesc_amd64_linux_no_xml.get ();
+      else
+#endif
+	return tdesc_i386_linux_no_xml.get ();
+    }
+
 #if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
   if (machine == EM_386 && have_ptrace_getfpxregs == -1)
     {
@@ -907,17 +923,6 @@ x86_linux_read_description (void)
     }
 #endif
 
-  if (!use_xml)
-    {
-      /* Don't use XML.  */
-#ifdef __x86_64__
-      if (machine == EM_X86_64)
-	return tdesc_amd64_linux_no_xml.get ();
-      else
-#endif
-	return tdesc_i386_linux_no_xml.get ();
-    }
-
   if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
                     ` (2 preceding siblings ...)
  2024-03-05 17:00   ` [PATCHv2 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 5/7] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Share the definition of I386_LINUX_XSAVE_XCR0_OFFSET between GDB and
gdbserver.

This commit is part of a series that aims to share more of the x86
target description creation code between GDB and gdbserver.  The
I386_LINUX_XSAVE_XCR0_OFFSET #define is used as part of the target
description creation, and I noticed that this constant is defined
separately for GDB and gdbserver.

This commit moves the definition into gdb/nat/x86-linux.h, which
allows the #define to be shared.

There should be no user visible changes after this commit.
---
 gdb/amd64-linux-tdep.c     |  1 +
 gdb/i386-linux-tdep.c      |  1 +
 gdb/i386-linux-tdep.h      | 20 --------------------
 gdb/nat/x86-linux.h        | 20 ++++++++++++++++++++
 gdbserver/linux-x86-low.cc | 22 +---------------------
 5 files changed, 23 insertions(+), 41 deletions(-)

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 1deb13b4e9a..a512ec5dd02 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -42,6 +42,7 @@
 #include "arch/amd64.h"
 #include "target-descriptions.h"
 #include "expop.h"
+#include "nat/x86-linux.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index a2f937690c2..8dd7203b6c3 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -40,6 +40,7 @@
 
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 5891747572b..07593c6a8ec 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -58,26 +58,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 /* Return the target description according to XCR0.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */ 
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 15153ea277e..855a8d14f91 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -22,6 +22,26 @@
 
 #include "nat/linux-nat.h"
 
+/* Format of XSAVE extended state is:
+	struct
+	{
+	  fxsave_bytes[0..463]
+	  sw_usable_bytes[464..511]
+	  xstate_hdr_bytes[512..575]
+	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
+	};
+
+  Same memory layout will be used for the coredump NT_X86_XSTATE
+  representing the XSAVE extended state registers.
+
+  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+  extended state mask, which is the same as the extended control register
+  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
+  together with the mask saved in the xstate_hdr_bytes to determine what
+  states the processor/OS supports and what state, used or initialized,
+  the process/thread is in.  */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
 /* Set whether our local mirror of LWP's debug registers has been
    changed since the values were last written to the thread.  Nonzero
    indicates that a change has been made, zero indicates no change.  */
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index ec80bfe905c..7bade8b3cbf 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -27,6 +27,7 @@
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-xstate.h"
 #include "nat/gdb_ptrace.h"
+#include "nat/x86-linux.h"
 
 #ifdef __x86_64__
 #include "nat/amd64-linux-siginfo.h"
@@ -832,27 +833,6 @@ x86_target::low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
 \f
 static int use_xml;
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  avx_bytes[576..831]
-	  future_state etc
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 /* Does the current host support the GETFPXREGS request?  The header
    file may or may not define it, and even if it is defined, the
    kernel will return EIO if it's running on a pre-SSE processor.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 5/7] gdb/gdbserver: share some code relating to target description creation
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
                     ` (3 preceding siblings ...)
  2024-03-05 17:00   ` [PATCHv2 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-05 17:00   ` [PATCHv2 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series to share more of the x86 target
description creation code between GDB and gdbserver.

Unlike previous commits which were mostly refactoring, this commit is
the first that makes a real change, though that change should mostly
be for gdbserver; I've largely adopted the "GDB" way of doing things
for gdbserver, and this fixes a real gdbserver bug.

On a x86-64 Linux target, running the test:

  gdb.server/connect-with-no-symbol-file.exp

results in two core files being created.  Both of these core files are
from the inferior process, created after gdbserver has detached.

In this test a gdbserver process is started and then, after gdbserver
has started, but before GDB attaches, we either delete the inferior
executable, or change its permissions so it can't be read.  Only after
doing this do we attempt to connect with GDB.

As GDB connects to gdbserver, gdbserver attempts to figure out the
target description so that it can send the description to GDB, this
involves a call to x86_linux_read_description.

In x86_linux_read_description one of the first things we do is try to
figure out if the process is 32-bit or 64-bit.  To do this we look up
the executable via the thread-id, and then attempt to read the
architecture size from the executable.  This isn't going to work if
the executable has been deleted, or is no longer readable.

And so, as we can't read the executable, we default to an i386 target
and use an i386 target description.

A consequence of using an i386 target description is that addresses
are assumed to be 32-bits.  Here's an example session that shows the
problems this causes.  This is run on an x86-64 machine, and the test
binary (xx.x) is a standard 64-bit x86-64 binary:

  shell_1$ gdbserver --once localhost :54321 /tmp/xx.x

  shell_2$ gdb -q
  (gdb) set sysroot
  (gdb) shell chmod 000 /tmp/xx.x
  (gdb) target remote :54321
  Remote debugging using :54321
  warning: /tmp/xx.x: Permission denied.
  0xf7fd3110 in ?? ()
  (gdb) show architecture
  The target architecture is set to "auto" (currently "i386").
  (gdb) p/x $pc
  $1 = 0xf7fd3110
  (gdb) info proc mappings
  process 2412639
  Mapped address spaces:

  	Start Addr   End Addr       Size     Offset  Perms   objfile
  	  0x400000   0x401000     0x1000        0x0  r--p   /tmp/xx.x
  	  0x401000   0x402000     0x1000     0x1000  r-xp   /tmp/xx.x
  	  0x402000   0x403000     0x1000     0x2000  r--p   /tmp/xx.x
  	  0x403000   0x405000     0x2000     0x2000  rw-p   /tmp/xx.x
  	0xf7fcb000 0xf7fcf000     0x4000        0x0  r--p   [vvar]
  	0xf7fcf000 0xf7fd1000     0x2000        0x0  r-xp   [vdso]
  	0xf7fd1000 0xf7fd3000     0x2000        0x0  r--p   /usr/lib64/ld-2.30.so
  	0xf7fd3000 0xf7ff3000    0x20000     0x2000  r-xp   /usr/lib64/ld-2.30.so
  	0xf7ff3000 0xf7ffb000     0x8000    0x22000  r--p   /usr/lib64/ld-2.30.so
  	0xf7ffc000 0xf7ffe000     0x2000    0x2a000  rw-p   /usr/lib64/ld-2.30.so
  	0xf7ffe000 0xf7fff000     0x1000        0x0  rw-p
  	0xfffda000 0xfffff000    0x25000        0x0  rw-p   [stack]
  	0xff600000 0xff601000     0x1000        0x0  r-xp   [vsyscall]
  (gdb) info inferiors
    Num  Description       Connection           Executable
  * 1    process 2412639   1 (remote :54321)
  (gdb) shell cat /proc/2412639/maps
  00400000-00401000 r--p 00000000 fd:03 45907133           /tmp/xx.x
  00401000-00402000 r-xp 00001000 fd:03 45907133           /tmp/xx.x
  00402000-00403000 r--p 00002000 fd:03 45907133           /tmp/xx.x
  00403000-00405000 rw-p 00002000 fd:03 45907133           /tmp/xx.x
  7ffff7fcb000-7ffff7fcf000 r--p 00000000 00:00 0          [vvar]
  7ffff7fcf000-7ffff7fd1000 r-xp 00000000 00:00 0          [vdso]
  7ffff7fd1000-7ffff7fd3000 r--p 00000000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7fd3000-7ffff7ff3000 r-xp 00002000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ff3000-7ffff7ffb000 r--p 00022000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffc000-7ffff7ffe000 rw-p 0002a000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  7ffffffda000-7ffffffff000 rw-p 00000000 00:00 0          [stack]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]
  (gdb)

Notice the difference between the mappings reported via GDB and those
reported directly from the kernel via /proc/PID/maps, the addresses of
every mapping is clamped to 32-bits for GDB, while the kernel reports
real 64-bit addresses.

Notice also that the $pc value is a 32-bit value.  It appears to be
within one of the mappings reported by GDB, but is outside any of the
mappings reported from the kernel.

And this is where the problem arises.  When gdbserver detaches from
the inferior we pass the inferior the address from which it should
resume.  Due to the 32/64 bit confusion we tell the inferior to resume
from the 32-bit $pc value, which is not within any valid mapping, and
so, as soon as the inferior resumes, it segfaults.

If we look at how GDB (not gdbserver) figures out its target
description then we see an interesting difference.  GDB doesn't try to
read the executable.  Instead GDB uses ptrace to query the thread's
state, and uses this to figure out the if the thread is 32 or 64 bit.

If we update gdbserver to do it the "GDB" way then the above problem
is resolved, gdbserver now sees the process as 64-bit, and when we
detach from the inferior we give it the correct 64-bit address, and
the inferior no longer segfaults.

Now, I could just update the gdbserver code, but better, I think, to
share one copy of the code between GDB and gdbserver in gdb/nat/.
That is what this commit does.

The cores of x86_linux_read_description from gdbserver and
x86_linux_nat_target::read_description from GDB are moved into a new
file gdb/nat/x86-linux-tdesc.c and combined into a single function
x86_linux_tdesc_for_tid which is called from each location.

This new function does things the GDB way, the only changes are to
allow for the sharing; we now have a callback function to call the
first time that the xcr0 state is read, this allows for GDB and
gdbserver to perform their own initialisation as needed, and
additionally, the new function takes a pointer for where to cache the
xcr0 value, this isn't needed for this commit, but will be useful in a
later commit where gdbserver will want to read this cached xcr0
value.

Another thing to note about this commit is how the functions
i386_linux_read_description and amd64_linux_read_description are
handled.  For now I've left these function as implemented separately
in GDB and gdbserver.  I've moved the declarations of these functions
into gdb/nat/x86-linux-tdesc.h, but the implementations are left as
separate.

A later commit in this series will make these functions shared too,
but doing this is not trivial, so I've left that for a separate
commit.  Merging the declarations as I've done here ensures that
everyone implements the function to the same API, and once these
functions are shared (in a later commit) we'll want a shared
declaration anyway.
---
 gdb/Makefile.in              |   1 +
 gdb/amd64-linux-tdep.c       |   1 +
 gdb/amd64-linux-tdep.h       |   6 --
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |   1 +
 gdb/i386-linux-tdep.h        |   3 -
 gdb/nat/x86-linux-tdesc.c    | 118 ++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  75 ++++++++++++++++++
 gdb/x86-linux-nat.c          |  91 ++++------------------
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 +
 gdbserver/linux-i386-ipa.cc  |   1 +
 gdbserver/linux-x86-low.cc   | 144 +++++++++++------------------------
 gdbserver/linux-x86-tdesc.cc |   1 +
 gdbserver/linux-x86-tdesc.h  |   7 --
 15 files changed, 261 insertions(+), 195 deletions(-)
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0e0f19c40c9..76bed92d84e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1586,6 +1586,7 @@ HFILES_NO_SRCDIR = \
 	nat/x86-gcc-cpuid.h \
 	nat/x86-linux.h \
 	nat/x86-linux-dregs.h \
+	nat/x86-linux-tdesc.h \
 	python/py-event.h \
 	python/py-events.h \
 	python/py-stopevent.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index a512ec5dd02..7e0900dc6f9 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -43,6 +43,7 @@
 #include "target-descriptions.h"
 #include "expop.h"
 #include "nat/x86-linux.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h
index 2003dcda78f..0ec49e7fe03 100644
--- a/gdb/amd64-linux-tdep.h
+++ b/gdb/amd64-linux-tdep.h
@@ -43,12 +43,6 @@ extern struct target_desc *tdesc_x32_linux;
 extern struct target_desc *tdesc_x32_avx_linux;
 extern struct target_desc *tdesc_x32_avx_avx512_linux;
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  */
-
-const target_desc *amd64_linux_read_description (uint64_t xcr0_features_bit,
-						 bool is_x32);
-
 /* Enum that defines the syscall identifiers for amd64 linux.
    Used for process record/replay, these will be translated into
    a gdb-canonical set of syscall ids in linux-record.c.  */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 8b98511cef7..4bcc0696027 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -322,7 +322,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o \
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
 		nat/amd64-linux-siginfo.o"
 		;;
 	    sparc)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 8dd7203b6c3..f5f7a36bf36 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -41,6 +41,7 @@
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 07593c6a8ec..e8691cd778e 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -55,9 +55,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 					   struct ui_out *uiout,
 					   enum gdb_signal siggnal);
 
-/* Return the target description according to XCR0.  */
-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
new file mode 100644
index 00000000000..78d22f55d67
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -0,0 +1,118 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "nat/x86-linux-tdesc.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#endif
+#include "arch/i386.h"
+
+#include "gdbsupport/common-defs.h"
+#include "nat/x86-linux.h"
+#include "nat/x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <linux/uio.h>
+#include <elf.h>
+#include <sys/user.h>
+#include <sys/user.h>
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage)
+{
+#ifdef __x86_64__
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  bool is_64bit = arch_size.is_64bit ();
+  bool is_x32 = arch_size.is_x32 ();
+
+  if (sizeof (void *) == 4 && is_64bit && !is_x32)
+    error ("%s", error_msg);
+
+#elif HAVE_PTRACE_GETFPXREGS
+  if (have_ptrace_getfpxregs == -1)
+    {
+      elf_fpxregset_t fpxregs;
+
+      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+	{
+	  have_ptrace_getfpxregs = 0;
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
+	}
+    }
+#endif
+
+  if (*have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    {
+      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, tid,
+		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
+	{
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  *xcr0_storage = 0;
+	}
+      else
+	{
+	  *have_ptrace_getregset = TRIBOOL_TRUE;
+
+	  /* Get XCR0 from XSAVE extended state.  */
+	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+				      / sizeof (uint64_t))];
+
+	  xcr0_init_cb (*xcr0_storage);
+	}
+    }
+
+  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
+     PTRACE_GETREGSET is not available then set xcr0_features_bits to
+     zero so that the "no-features" descriptions are returned by the
+     switches below.  */
+  uint64_t xcr0_features_bits;
+  if (*have_ptrace_getregset == TRIBOOL_TRUE)
+    xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+  else
+    xcr0_features_bits = 0;
+
+#ifdef __x86_64__
+  if (is_64bit)
+    {
+      return amd64_linux_read_description (xcr0_features_bits, is_x32);
+    }
+  else
+#endif
+    return i386_linux_read_description (xcr0_features_bits);
+
+  gdb_assert_not_reached ("failed to return tdesc");
+}
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
new file mode 100644
index 00000000000..3727a8bf95e
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -0,0 +1,75 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_X86_LINUX_TDESC_H
+#define NAT_X86_LINUX_TDESC_H
+
+#include "gdbsupport/function-view.h"
+
+struct target_desc;
+
+/* Return the target description for Linux thread TID.
+
+   When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
+   xcr0 is read using ptrace calls and stored into *XCR0_STORAGE.  Then
+   XCR0_INIT_CB is called with the value of *XCR0_STORAGE and
+   *HAVE_PTRACE_GETREGSET is set to TRIBOOL_TRUE.
+
+   If the attempt to read xcr0 using ptrace fails then *XCR0_STORAGE is set
+   to zero and *HAVE_PTRACE_GETREGSET is set to TRIBOOL_FALSE.
+
+   The storage pointed to by XCR0_STORAGE must exist until the program
+   terminates, this storage is used to cache the xcr0 value.  As such
+   XCR0_INIT_CB will only be called once if xcr0 is successfully read using
+   ptrace, or not at all if the ptrace call fails.
+
+   This function returns a target description based on the extracted xcr0
+   value along with other characteristics of the thread identified by TID.
+
+   This function can return nullptr if we encounter a machine configuration
+   for which a target_desc cannot be created.  Ideally this would not be
+   the case, we should be able to create a target description for every
+   possible machine configuration.  See amd64_linux_read_description and
+   i386_linux_read_description for cases when nullptr might be
+   returned.
+
+   ERROR_MSG is using in an error() call if we try to create a target
+   description for a 64-bit process but this is a 32-bit build of GDB.  */
+
+extern const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage);
+
+#ifdef __x86_64__
+
+/* Return the right amd64-linux target descriptions according to
+   XCR0_FEATURES_BIT and IS_X32.  This is implemented separately in both
+   GDB and gdbserver.  */
+
+extern const target_desc *amd64_linux_read_description
+	(uint64_t xcr0_features_bit, bool is_x32);
+
+#endif
+
+/* Return the target description according to XCR0.  This is implemented
+   separately in both GDB and gdbserver.  */
+extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+
+#endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b39d05c401f..872e27e739f 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -42,6 +42,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "nat/linux-ptrace.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* linux_nat_target::low_new_fork implementation.  */
 
@@ -96,90 +97,26 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
 const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
+  static uint64_t xcr0_storage;
 
   if (inferior_ptid == null_ptid)
     return this->beneath ()->read_description ();
 
-  tid = inferior_ptid.pid ();
-
-#ifdef __x86_64__
-
-  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
-  is_64bit = arch_size.is_64bit ();
-  is_x32 = arch_size.is_x32 ();
-
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
-	}
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  m_xsave_layout = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset == TRIBOOL_TRUE)
-    xcr0_features_bits = xcr0 & X86_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      return amd64_linux_read_description (xcr0_features_bits, is_x32);
-#endif
-    }
-  else
-    {
-      const struct target_desc * tdesc
-	= i386_linux_read_description (xcr0_features_bits);
+  int tid = inferior_ptid.pid ();
 
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE_MASK);
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDB");
 
-      return tdesc;
-    }
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [&] (uint64_t xcr0)
+  {
+    this->m_xsave_layout
+      = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+  };
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb,
+				  error_msg, &xcr0_storage);
 }
 \f
 
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 9e861a75088..7a2702d78bf 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -109,6 +109,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -371,6 +372,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 54e4c9812bb..f97b0d6a1d9 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -23,6 +23,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* Defined in auto-generated file amd64-linux.c.  */
 void init_registers_amd64_linux (void);
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 2e4646f8c03..459b8055b5c 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -23,6 +23,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 7bade8b3cbf..9bf369f8a34 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -48,6 +48,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "linux-x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
 static target_desc_up tdesc_amd64_linux_no_xml;
@@ -844,32 +845,20 @@ int have_ptrace_getfpxregs =
 #endif
 ;
 
+/* Cached xcr0 value.  This is initialised the first time
+   x86_linux_read_description is called.  */
+
+static uint64_t xcr0_storage;
+
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
 x86_linux_read_description (void)
 {
-  unsigned int machine;
-  int is_elf64;
-  int xcr0_features;
-  int tid;
-  static uint64_t xcr0;
-  static int xsave_len;
-  struct regset_info *regset;
-
-  tid = lwpid_of (current_thread);
+  int tid = lwpid_of (current_thread);
 
-  is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
-
-  if (sizeof (void *) == 4)
-    {
-      if (is_elf64 > 0)
-       error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
-      else if (machine == EM_X86_64)
-       error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
-    }
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDBserver");
 
   /* If we are not allowed to send an XML target description then we need
      to use the hard-wired target descriptions.  This corresponds to GDB's
@@ -879,99 +868,54 @@ x86_linux_read_description (void)
      generate some alternative target descriptions.  */
   if (!use_xml)
     {
+      x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+      bool is_64bit = arch_size.is_64bit ();
+      bool is_x32 = arch_size.is_x32 ();
+
+      if (sizeof (void *) == 4 && is_64bit && !is_x32)
+	error ("%s", error_msg);
+
 #ifdef __x86_64__
-      if (machine == EM_X86_64)
+      if (is_64bit && !is_x32)
 	return tdesc_amd64_linux_no_xml.get ();
       else
 #endif
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
-  if (machine == EM_386 && have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87);
-	}
-      else
-	have_ptrace_getfpxregs = 1;
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  xsave_len = x86_xsave_length ();
-
-	  /* Use PTRACE_GETREGSET if it is available.  */
-	  for (regset = x86_regsets;
-	       regset->fill_function != NULL; regset++)
-	    if (regset->get_request == PTRACE_GETREGSET)
-	      regset->size = xsave_len;
-	    else if (regset->type != GENERAL_REGS)
-	      regset->size = 0;
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
-		   && (xcr0 & X86_XSTATE_ALL_MASK));
-
-  if (xcr0_features)
-    i387_set_xsave_mask (xcr0, xsave_len);
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [] (uint64_t xcr0)
+  {
+    i387_set_xsave_mask (xcr0, x86_xsave_length ());
+  };
 
-  if (machine == EM_X86_64)
-    {
-#ifdef __x86_64__
-      const target_desc *tdesc = NULL;
+  /* If have_ptrace_getregset is changed to true by calling
+     x86_linux_tdesc_for_tid then we will perform some additional
+     initialisation.  */
+  bool have_ptrace_getregset_is_unknown
+    = have_ptrace_getregset == TRIBOOL_UNKNOWN;
 
-      if (xcr0_features)
-	{
-	  tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
-						!is_elf64);
-	}
+  const target_desc *tdesc
+    = x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb, error_msg,
+			       &xcr0_storage);
 
-      if (tdesc == NULL)
-	tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
-      return tdesc;
-#endif
-    }
-  else
+  if (have_ptrace_getregset_is_unknown
+      && have_ptrace_getregset == TRIBOOL_TRUE)
     {
-      const target_desc *tdesc = NULL;
-
-      if (xcr0_features)
-	  tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
-
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE);
-
-      return tdesc;
+      int xsave_len = x86_xsave_length ();
+
+      /* Use PTRACE_GETREGSET if it is available.  */
+      for (regset_info *regset = x86_regsets;
+	   regset->fill_function != nullptr;
+	   regset++)
+	if (regset->get_request == PTRACE_GETREGSET)
+	  regset->size = xsave_len;
+	else if (regset->type != GENERAL_REGS)
+	  regset->size = 0;
     }
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return tdesc;
 }
 
 /* Update all the target description of all processes; a new GDB
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 626207fc477..9fd64d8574b 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -26,6 +26,7 @@
 #include "arch/amd64.h"
 #endif
 #include "x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* Return the right x86_linux_tdesc index for a given XCR0.  Return
    X86_TDESC_LAST if can't find a match.  */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index f9561b129ae..576aaf5e165 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -46,11 +46,4 @@ int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
 
 const struct target_desc *i386_get_ipa_tdesc (int idx);
 
-#ifdef __x86_64__
-const struct target_desc *amd64_linux_read_description (uint64_t xcr0,
-							bool is_x32);
-#endif
-
-const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 6/7] gdbserver: update target description creation for x86/linux
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
                     ` (4 preceding siblings ...)
  2024-03-05 17:00   ` [PATCHv2 5/7] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-19 16:01     ` John Baldwin
  2024-03-05 17:00   ` [PATCHv2 7/7] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
                     ` (2 subsequent siblings)
  8 siblings, 1 reply; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series which aims to share more of the target
description creation between GDB and gdbserver for x86/Linux.

After some refactoring, the previous commit actually started to share
some code, we added the shared x86_linux_tdesc_for_tid function into
nat/x86-linux-tdesc.c.  However, this function still relies on
amd64_linux_read_description and i386_linux_read_description which are
implemented separately for both gdbserver and GDB.  Given that at
their core, all these functions to is:

  1. take an xcr0 value as input,
  2. mask out some feature bits,
  3. look for a cached pre-generated target description and return it
     if found,
  4. if no cached target description is found then call either
     amd64_create_target_description or
     i386_create_target_description to create a new target
     description, which is then added to the cache.  Return the newly
     created target description.

The inner functions amd64_create_target_description and
i386_create_target_description are already shared between GDB and
gdbserver (in the gdb/arch/ directory), so the only thing that
the *_read_description functions really do is add the caching layer,
and it feels like this really could be shared.

However, we have a small problem.

On the GDB side we create target descriptions using a different set of
cpu features than on the gdbserver side!  This means that for the
exact same target, we might get a different target description when
using native GDB vs using gdbserver.  This surely feels like a
mistake, I would expect to get the same target description on each.

The table below shows the number of possible different target
descriptions that we can create on the GDB side vs on the gdbserver
side for each target type:

        | GDB | gdbserver
  ------|-----|----------
  i386  | 64  | 7
  amd64 | 32  | 7
  x32   | 16  | 7

So in theory, all I want to do is move the GDB version
of *_read_description into the nat/ directory and have gdbserver use
that, then both GDB and gdbserver would be able to create any of the
possible target descriptions.

Unfortunately it's a little more complex than that due to the in
process agent (IPA).

When the IPA is in use, gdbserver sends a target description index to
the IPA, and the IPA uses this to find the correct target description
to use.

** START OF AN ASIDE **

Back in the day I suspect this approach made perfect sense.  However
since this commit:

  commit a8806230241d201f808d856eaae4d44088117b0c
  Date:   Thu Dec 7 17:07:01 2017 +0000

      Initialize target description early in IPA

I think passing the index is now more trouble than its worth.

We used to pass the index, and then use that index to lookup which
target description to instantiate and use.  However, the above commit
fixed an issue where we can't call malloc() within (certain parts of)
the IPA (apparently), so instead we now pre-compute _every_ possible
target description within the IPA.  The index is now only used to
lookup which of the (many) pre-computed target descriptions to use.

It would (I think) have been easier all around if the IPA just
self-inspected, figured out its own xcr0 value, and used that to
create the one target description that is required.  So long as the
xcr0 to target description code is shared (at compile time) with
gdbserver, then we can be sure that the IPA will derive the same
target description as gdbserver, and we would avoid all this index
passing business, which has made this commit so very, very painful.

** END OF AN ASIDE **

Currently then for x86/linux, gdbserver sends a number between 0 and 7
to the IPA, and the IPA uses this to create a target description.

However, I am proposing that gdbserver should now create one of (up
to) 64 different target descriptions for i386, so this 0 to 7 index
isn't going to be good enough any more (amd64 and x32 have slightly
fewer possible target descriptions, but still more than 8, so the
problem is the same).

For a while I wondered if I was going to have to try and find some
backward compatible solution for this mess.  But after seeing how
lightly the IPA is actually documented, I wonder if it is not the case
that there is a tight coupling between a version of gdbserver and a
version of the IPA?  At least I'm hoping so.

In this commit I have thrown out the old IPA target description index
numbering scheme, and switched to a completely new numbering scheme.
Instead of the index that is passed being arbitrary, the index is
instead calculated from the set of cpu features that are present on
the target.  Within the IPA we can then reverse this logic to recreate
the xcr0 value based on the index, and from the xcr0 value we can
create the correct target description.

With the gdbserver to IPA numbering scheme issue resolved I have then
update the gdbserver versions of amd64_linux_read_description and
i386_linux_read_description so that they create target descriptions
using the same set of cpu features as GDB itself.

After this gdbserver should now always come up with the same target
description as GDB does on any x86/Linux target.

This commit does not introduce any new code sharing between GDB and
gdbserver as previous commits in this series does.  Instead this
commit is all about bringing GDB and gdbserver into alignment
functionally so that the next commit can merge the GDB and gdbserver
versions of these functions.
---
 gdbserver/linux-amd64-ipa.cc |  43 +----
 gdbserver/linux-i386-ipa.cc  |  23 +--
 gdbserver/linux-x86-low.cc   |  15 +-
 gdbserver/linux-x86-tdesc.cc | 315 +++++++++++++++++++++++++----------
 gdbserver/linux-x86-tdesc.h  |  49 +++---
 5 files changed, 277 insertions(+), 168 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index f97b0d6a1d9..42de7d2da16 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -169,47 +169,21 @@ supply_static_tracepoint_registers (struct regcache *regcache,
 
 #endif /* HAVE_UST */
 
-#if !defined __ILP32__
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-#endif
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
 
 #if defined __ILP32__
-  switch (idx)
-    {
-    case X86_TDESC_SSE:
-      return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-    case X86_TDESC_AVX:
-      return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-    case X86_TDESC_AVX_AVX512:
-      return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
-    default:
-      break;
-    }
+  bool is_x32 = true;
 #else
-  return amd64_linux_read_description (idx2mask[idx], false);
+  bool is_x32 = false;
 #endif
 
-  internal_error ("unknown ipa tdesc index: %d", idx);
+  return amd64_linux_read_description (xcr0, is_x32);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a
@@ -277,11 +251,10 @@ void
 initialize_low_tracepoint (void)
 {
 #if defined __ILP32__
-  amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+  for (auto i = 0; i < x86_linux_x32_ipa_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
 #else
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    amd64_linux_read_description (idx2mask[i], false);
+  for (auto i = 0; i < x86_linux_amd64_ipa_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
 #endif
 }
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 459b8055b5c..246bcb9813b 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -246,28 +246,15 @@ initialize_fast_tracepoint_trampoline_buffer (void)
     }
 }
 
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
-  return i386_linux_read_description (idx2mask[idx]);
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+
+  return i386_linux_read_description (xcr0);
 }
 
 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
@@ -289,6 +276,6 @@ void
 initialize_low_tracepoint (void)
 {
   initialize_fast_tracepoint_trampoline_buffer ();
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    i386_linux_read_description (idx2mask[i]);
+  for (auto i = 0; i < x86_linux_i386_ipa_tdesc_count (); i++)
+    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
 }
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 9bf369f8a34..6dbefdb2f26 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -2892,14 +2892,17 @@ x86_target::get_ipa_tdesc_idx ()
   struct regcache *regcache = get_thread_regcache (current_thread, 0);
   const struct target_desc *tdesc = regcache->tdesc;
 
+  if (!use_xml)
+    {
+      if (tdesc == tdesc_i386_linux_no_xml.get ()
 #ifdef __x86_64__
-  return amd64_get_ipa_tdesc_idx (tdesc);
-#endif
-
-  if (tdesc == tdesc_i386_linux_no_xml.get ())
-    return X86_TDESC_SSE;
+	  || tdesc == tdesc_amd64_linux_no_xml.get ()
+#endif /* __x86_64__ */
+	  )
+	return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+    }
 
-  return i386_get_ipa_tdesc_idx (tdesc);
+  return x86_linux_xcr0_to_tdesc_idx (xcr0_storage);
 }
 
 /* The linux target ops object.  */
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 9fd64d8574b..87cf368a336 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -28,96 +28,277 @@
 #include "x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
-/* Return the right x86_linux_tdesc index for a given XCR0.  Return
-   X86_TDESC_LAST if can't find a match.  */
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
 
-static enum x86_linux_tdesc
-xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
 {
-  if (xcr0 & X86_XSTATE_PKRU)
-    {
-      if (is_x32)
-	{
-	  /* No x32 MPX and PKU, fall back to avx_avx512.  */
-	  return X86_TDESC_AVX_AVX512;
-	}
-      else
-	return X86_TDESC_AVX_MPX_AVX512_PKU;
-    }
-  else if (xcr0 & X86_XSTATE_AVX512)
-    return X86_TDESC_AVX_AVX512;
-  else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_ipa_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_ipa_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_ipa_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_AVX_MPX;
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
+	idx |= (1 << i);
     }
-  else if (xcr0 & X86_XSTATE_MPX)
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_MPX;
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
     }
-  else if (xcr0 & X86_XSTATE_AVX)
-    return X86_TDESC_AVX;
-  else if (xcr0 & X86_XSTATE_SSE)
-    return X86_TDESC_SSE;
-  else if (xcr0 & X86_XSTATE_X87)
-    return X86_TDESC_MMX;
-  else
-    return X86_TDESC_LAST;
+
+  return xcr0;
 }
 
+#endif /* IN_PROCESS_AGENT */
+
 #if defined __i386__ || !defined IN_PROCESS_AGENT
 
-static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible i386 target descriptions.  */
 
-/* Return the target description according to XCR0.  */
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 i386_linux_read_description (uint64_t xcr0)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
 
-  struct target_desc **tdesc = &i386_tdescs[idx];
+  target_desc **tdesc = &i386_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = i386_create_target_description (xcr0, true, false);
 
       init_target_desc (*tdesc, i386_expedite_regs);
     }
 
-  return *tdesc;;
+  return *tdesc;
 }
 #endif
 
 #ifdef __x86_64__
 
-static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
-static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
 
-  struct target_desc **tdesc = NULL;
+  target_desc **tdesc = nullptr;
 
   if (is_x32)
     tdesc = &x32_tdescs[idx];
   else
     tdesc = &amd64_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
 
@@ -127,39 +308,3 @@ amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 }
 
 #endif
-
-#ifndef IN_PROCESS_AGENT
-
-int
-i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == i386_tdescs[i])
-	return i;
-    }
-
-  /* If none tdesc is found, return the one with minimum features.  */
-  return X86_TDESC_MMX;
-}
-
-#if defined __x86_64__
-int
-amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == amd64_tdescs[i])
-	return i;
-    }
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == x32_tdescs[i])
-	return i;
-    }
-
-  return X86_TDESC_SSE;
-}
-
-#endif
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index 576aaf5e165..70456e4be44 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -21,29 +21,30 @@
 #ifndef GDBSERVER_LINUX_X86_TDESC_H
 #define GDBSERVER_LINUX_X86_TDESC_H
 
-/* Note: since IPA obviously knows what ABI it's running on (i386 vs x86_64
-   vs x32), it's sufficient to pass only the register set here.  This,
-   together with the ABI known at IPA compile time, maps to a tdesc.  */
-
-enum x86_linux_tdesc {
-  X86_TDESC_MMX = 0,
-  X86_TDESC_SSE = 1,
-  X86_TDESC_AVX = 2,
-  X86_TDESC_MPX = 3,
-  X86_TDESC_AVX_MPX = 4,
-  X86_TDESC_AVX_AVX512 = 5,
-  X86_TDESC_AVX_MPX_AVX512_PKU = 6,
-  X86_TDESC_LAST = 7,
-};
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-int i386_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-#if defined __x86_64__ && !defined IN_PROCESS_AGENT
-int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-const struct target_desc *i386_get_ipa_tdesc (int idx);
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_ipa_tdesc_count ();
+extern int x86_linux_x32_ipa_tdesc_count ();
+extern int x86_linux_i386_ipa_tdesc_count ();
+
+
+#endif /* IN_PROCESS_AGENT */
 
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 7/7] gdb/gdbserver: share x86/linux tdesc caching
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
                     ` (5 preceding siblings ...)
  2024-03-05 17:00   ` [PATCHv2 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
@ 2024-03-05 17:00   ` Andrew Burgess
  2024-03-19 16:05   ` [PATCHv2 0/7] x86/Linux Target Description Changes John Baldwin
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
  8 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-05 17:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit builds on the previous series of commits to share the
target description caching code between GDB and gdbserver for
x86/Linux targets.

The objective of this commit is to move the four functions (2 each of)
i386_linux_read_description and amd64_linux_read_description into
gdb/nat/x86-linux-tdesc.c and combine them so we have just a single
copy of each.  Then both GDB and gdbserver will link against these
shared functions.

It is worth reading the description of the previous commit to see why
this merging is not as simple as it seems: on the gdbserver side we
actually have two users of these functions, gdbserver itself, and the
in process agent (IPA).

However, the previous commit streamlined the gdbserver code, and so
now it is simple to move the two functions along with all their
support functions from the gdbserver directory into the gdb/nat/
directory, and then GDB is fine to call these functions.

One small curiosity with this patch is the function
x86_linux_post_init_tdesc.  On the gdbserver side the two functions
amd64_linux_read_description and i386_linux_read_description have some
functionality that is not present on the GDB side, that is some
additional configuration that is performed as each target description
is created to setup the expedited registers.

To support this I've added the function x86_linux_post_init_tdesc.
This function is called from the two *_linux_read_description
functions, but is implemented separately for GDB and gdbserver.

This does mean adding back some non-shared code when this whole series
has been about sharing code, but now the only non-shared bit is the
single line that is actually different between GDB and gdbserver, all
the rest, which is identical, is now shared.

I did need to add a new rule to the gdbserver Makefile, this is to
allow the nat/x86-linux-tdesc.c file to be compiled for the IPA.
---
 gdb/amd64-linux-tdep.c       |  31 ----
 gdb/i386-linux-tdep.c        |  30 +---
 gdb/nat/x86-linux-tdesc.c    | 287 +++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  56 ++++++-
 gdbserver/Makefile.in        |   4 +
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 -
 gdbserver/linux-i386-ipa.cc  |   1 -
 gdbserver/linux-x86-low.cc   |   1 -
 gdbserver/linux-x86-tdesc.cc | 285 +---------------------------------
 gdbserver/linux-x86-tdesc.h  |  50 ------
 11 files changed, 355 insertions(+), 393 deletions(-)
 delete mode 100644 gdbserver/linux-x86-tdesc.h

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 7e0900dc6f9..e149034de21 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1579,37 +1579,6 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
-const target_desc *
-amd64_linux_read_description (uint64_t xcr0_features_bit, bool is_x32)
-{
-  static target_desc *amd64_linux_tdescs \
-    [2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  static target_desc *x32_linux_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/] = {};
-
-  target_desc **tdesc;
-
-  if (is_x32)
-    {
-      tdesc = &x32_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0 ]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-  else
-    {
-      tdesc = &amd64_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_MPX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-
-  if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0_features_bit, is_x32,
-					      true, true);
-
-  return *tdesc;
-}
-
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index f5f7a36bf36..ed1442236e8 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -681,29 +681,12 @@ i386_linux_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
 					  layout) != 0;
 }
 
-/* See i386-linux-tdep.h.  */
+/* See nat/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  if (xcr0 == 0)
-    return NULL;
-
-  static struct target_desc *i386_linux_tdescs \
-    [2/*X87*/][2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  struct target_desc **tdesc;
-
-  tdesc = &i386_linux_tdescs[(xcr0 & X86_XSTATE_X87) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
-
-  if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true, false);
-
-  return *tdesc;
+  /* Nothing.  */
 }
 
 /* Get Linux/x86 target description from core dump.  */
@@ -716,7 +699,10 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
   /* Linux/i386.  */
   x86_xsave_layout layout;
   uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
-  const struct target_desc *tdesc = i386_linux_read_description (xcr0);
+
+  const struct target_desc *tdesc;
+  if (xcr0 != 0)
+    tdesc = i386_linux_read_description (xcr0);
 
   if (tdesc != NULL)
     return tdesc;
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index 78d22f55d67..297d1873680 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -38,6 +38,8 @@
 #include <sys/user.h>
 #include <sys/user.h>
 
+#ifndef IN_PROCESS_AGENT
+
 /* See nat/x86-linux-tdesc.h.  */
 
 const target_desc *
@@ -116,3 +118,288 @@ x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
 
   gdb_assert_not_reached ("failed to return tdesc");
 }
+
+#endif /* !IN_PROCESS_AGENT */
+
+
+
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
+
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_ipa_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_ipa_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_ipa_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
+	idx |= (1 << i);
+    }
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
+    }
+
+  return xcr0;
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+#if defined __i386__ || !defined IN_PROCESS_AGENT
+
+/* A cache of all possible i386 target descriptions.  */
+
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const struct target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
+
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
+
+  target_desc **tdesc = &i386_tdescs[idx];
+
+  if (*tdesc == nullptr)
+    {
+      *tdesc = i386_create_target_description (xcr0, true, false);
+
+      x86_linux_post_init_tdesc (*tdesc, false);
+    }
+
+  return *tdesc;
+}
+#endif
+
+#ifdef __x86_64__
+
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+{
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
+
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
+
+  target_desc **tdesc = nullptr;
+
+  if (is_x32)
+    tdesc = &x32_tdescs[idx];
+  else
+    tdesc = &amd64_tdescs[idx];
+
+  if (*tdesc == nullptr)
+    {
+      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
+
+      x86_linux_post_init_tdesc (*tdesc, true);
+    }
+  return *tdesc;
+}
+
+#endif
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
index 3727a8bf95e..648fe0ed0b2 100644
--- a/gdb/nat/x86-linux-tdesc.h
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -24,6 +24,8 @@
 
 struct target_desc;
 
+#ifndef IN_PROCESS_AGENT
+
 /* Return the target description for Linux thread TID.
 
    When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
@@ -57,19 +59,57 @@ x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
 			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
 			 const char *error_msg, uint64_t *xcr0_storage);
 
+#endif /* !IN_PROCESS_AGENT */
+
 #ifdef __x86_64__
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  This is implemented separately in both
-   GDB and gdbserver.  */
+/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
 
-extern const target_desc *amd64_linux_read_description
-	(uint64_t xcr0_features_bit, bool is_x32);
+extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
+							bool is_x32);
 
-#endif
+#endif /* __x86_64__ */
+
+/* Return the i386 target description corresponding to XCR0.  */
 
-/* Return the target description according to XCR0.  This is implemented
-   separately in both GDB and gdbserver.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
+/* This function is called from amd64_linux_read_description and
+   i386_linux_read_description after a new target description has been
+   created, TDESC is the new target description, IS_64BIT will be true
+   when called from amd64_linux_read_description, otherwise IS_64BIT will
+   be false.  If the *_linux_read_description functions found a cached
+   target description then this function will not be called.
+
+   Both GDB and gdbserver have their own implementations of this
+   function.  */
+
+extern void x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit);
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_ipa_tdesc_count ();
+extern int x86_linux_x32_ipa_tdesc_count ();
+extern int x86_linux_i386_ipa_tdesc_count ();
+
+#endif /* IN_PROCESS_AGENT */
+
 #endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index d12f8746611..c97649378e8 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -517,6 +517,10 @@ gdbsupport/%-ipa.o: ../gdbsupport/%.cc
 	$(IPAGENT_COMPILE) $<
 	$(POSTCOMPILE)
 
+nat/%-ipa.o: ../gdb/nat/%.c
+	$(IPAGENT_COMPILE) $<
+	$(POSTCOMPILE)
+
 %-ipa.o: ../gdb/%.c
 	$(IPAGENT_COMPILE) -x c++ $<
 	$(POSTCOMPILE)
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7a2702d78bf..36a457f21ed 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -116,6 +116,7 @@ case "${gdbserver_host}" in
 			srv_linux_btrace=yes
 			ipa_obj="linux-i386-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/i386-ipa.o"
+			ipa_obj="${ipa_obj} nat/x86-linux-tdesc-ipa.o"
 			;;
   i[34567]86-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
@@ -380,6 +381,7 @@ case "${gdbserver_host}" in
 			srv_linux_btrace=yes
 			ipa_obj="linux-amd64-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/amd64-ipa.o"
+			ipa_obj="${ipa_obj} nat/x86-linux-tdesc-ipa.o"
 			;;
   x86_64-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o"
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 42de7d2da16..afa6aab5bab 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -21,7 +21,6 @@
 #include "server.h"
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux-tdesc.h"
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 246bcb9813b..f6d26f784af 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -21,7 +21,6 @@
 #include "server.h"
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux-tdesc.h"
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 6dbefdb2f26..7134d20dd7f 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -47,7 +47,6 @@
 #include "nat/linux-nat.h"
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 87cf368a336..39d1d51b8c0 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -19,292 +19,19 @@
 
 #include "server.h"
 #include "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#endif
 #include "x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
-/* A structure used to describe a single cpu feature that might, or might
-   not, be checked for when creating a target description for one of i386,
-   amd64, or x32.  */
-
-struct x86_tdesc_feature {
-  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
-  uint64_t feature;
-
-  /* Is this feature checked when creating an i386 target description.  */
-  bool is_i386;
-
-  /* Is this feature checked when creating an amd64 target description.  */
-  bool is_amd64;
-
-  /* Is this feature checked when creating an x32 target description.  */
-  bool is_x32;
-};
-
-/* A constant table that describes all of the cpu features that are
-   checked when building a target description for i386, amd64, or x32.  */
-
-static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
-  /* Feature,           i386,	amd64,	x32.  */
-  { X86_XSTATE_PKRU,	true,	true, 	true },
-  { X86_XSTATE_AVX512,	true,	true, 	true },
-  { X86_XSTATE_AVX,	true,	true, 	true },
-  { X86_XSTATE_MPX,	true,	true, 	false },
-  { X86_XSTATE_SSE,	true,	false, 	false },
-  { X86_XSTATE_X87,	true,	false, 	false }
-};
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an i386 target description.  */
-
-static constexpr uint64_t
-x86_linux_i386_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an amd64 target description.  */
-
-static constexpr uint64_t
-x86_linux_amd64_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an x32 target description.  */
-
-static constexpr uint64_t
-x86_linux_x32_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an i386 target description.  */
-
-static constexpr int
-x86_linux_i386_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an amd64 target description.  */
-
-static constexpr int
-x86_linux_amd64_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an x32 target description.  */
-
-static constexpr int
-x86_linux_x32_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_amd64_ipa_tdesc_count ()
-{
-  return x86_linux_amd64_tdesc_count ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_x32_ipa_tdesc_count ()
-{
-  return x86_linux_x32_tdesc_count ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_i386_ipa_tdesc_count ()
-{
-  return x86_linux_i386_tdesc_count ();
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
-{
-  /* The following table shows which features are checked for when creating
-     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
-     represents the bit order within the generated index number.
-
-     i386  | x87 sse mpx avx avx512 pkru
-     amd64 |         mpx avx avx512 pkru
-     i32   |             avx avx512 pkru
-
-     The features are ordered so that for each mode (i386, amd64, i32) the
-     generated index will form a continuous range.  */
-
-  int idx = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
-	idx |= (1 << i);
-    }
-
-  return idx;
-}
-
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
-{
-  uint64_t xcr0 = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_tdesc_features[i].feature;
-    }
-
-  return xcr0;
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-/* A cache of all possible i386 target descriptions.  */
-
-static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
-
 /* See nat/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
-
-  target_desc **tdesc = &i386_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = i386_create_target_description (xcr0, true, false);
-
-      init_target_desc (*tdesc, i386_expedite_regs);
-    }
-
-  return *tdesc;
-}
-#endif
-
 #ifdef __x86_64__
-
-/* A cache of all possible amd64 target descriptions.  */
-
-static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
-
-/* A cache of all possible x32 target descriptions.  */
-
-static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
-  if (is_x32)
-    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  if (is_64bit)
+    init_target_desc (tdesc, amd64_expedite_regs);
   else
-    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
-
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  if (is_x32)
-    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
-  else
-    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
-
-  target_desc **tdesc = nullptr;
-
-  if (is_x32)
-    tdesc = &x32_tdescs[idx];
-  else
-    tdesc = &amd64_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
-      init_target_desc (*tdesc, amd64_expedite_regs);
-    }
-  return *tdesc;
+#endif
+    init_target_desc (tdesc, i386_expedite_regs);
 }
 
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
deleted file mode 100644
index 70456e4be44..00000000000
--- a/gdbserver/linux-x86-tdesc.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Low level support for x86 (i386 and x86-64), shared between gdbserver
-   and IPA.
-
-   Copyright (C) 2016-2024 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   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 <http://www.gnu.org/licenses/>.  */
-
-#ifndef GDBSERVER_LINUX_X86_TDESC_H
-#define GDBSERVER_LINUX_X86_TDESC_H
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
-
-/* Within the in-process-agent we need to pre-initialise all of the target
-   descriptions, to do this we need to know how many target descriptions
-   there are for each different target type.  These functions return the
-   target description count for the relevant target.  */
-
-extern int x86_linux_amd64_ipa_tdesc_count ();
-extern int x86_linux_x32_ipa_tdesc_count ();
-extern int x86_linux_i386_ipa_tdesc_count ();
-
-
-#endif /* IN_PROCESS_AGENT */
-
-#endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/7] gdbserver: update target description creation for x86/linux
  2024-03-05 17:00   ` [PATCHv2 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
@ 2024-03-19 16:01     ` John Baldwin
  2024-03-19 18:34       ` Andrew Burgess
  0 siblings, 1 reply; 65+ messages in thread
From: John Baldwin @ 2024-03-19 16:01 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 3/5/24 9:00 AM, Andrew Burgess wrote:
> This commit is part of a series which aims to share more of the target
> description creation between GDB and gdbserver for x86/Linux.
> 
> After some refactoring, the previous commit actually started to share
> some code, we added the shared x86_linux_tdesc_for_tid function into
> nat/x86-linux-tdesc.c.  However, this function still relies on
> amd64_linux_read_description and i386_linux_read_description which are
> implemented separately for both gdbserver and GDB.  Given that at
> their core, all these functions to is:
> 
>    1. take an xcr0 value as input,
>    2. mask out some feature bits,
>    3. look for a cached pre-generated target description and return it
>       if found,
>    4. if no cached target description is found then call either
>       amd64_create_target_description or
>       i386_create_target_description to create a new target
>       description, which is then added to the cache.  Return the newly
>       created target description.
> 
> The inner functions amd64_create_target_description and
> i386_create_target_description are already shared between GDB and
> gdbserver (in the gdb/arch/ directory), so the only thing that
> the *_read_description functions really do is add the caching layer,
> and it feels like this really could be shared.
> 
> However, we have a small problem.
> 
> On the GDB side we create target descriptions using a different set of
> cpu features than on the gdbserver side!  This means that for the
> exact same target, we might get a different target description when
> using native GDB vs using gdbserver.  This surely feels like a
> mistake, I would expect to get the same target description on each.
> 
> The table below shows the number of possible different target
> descriptions that we can create on the GDB side vs on the gdbserver
> side for each target type:
> 
>          | GDB | gdbserver
>    ------|-----|----------
>    i386  | 64  | 7
>    amd64 | 32  | 7
>    x32   | 16  | 7
> 
> So in theory, all I want to do is move the GDB version
> of *_read_description into the nat/ directory and have gdbserver use
> that, then both GDB and gdbserver would be able to create any of the
> possible target descriptions.
> 
> Unfortunately it's a little more complex than that due to the in
> process agent (IPA).
> 
> When the IPA is in use, gdbserver sends a target description index to
> the IPA, and the IPA uses this to find the correct target description
> to use.
> 
> ** START OF AN ASIDE **
> 
> Back in the day I suspect this approach made perfect sense.  However
> since this commit:
> 
>    commit a8806230241d201f808d856eaae4d44088117b0c
>    Date:   Thu Dec 7 17:07:01 2017 +0000
> 
>        Initialize target description early in IPA
> 
> I think passing the index is now more trouble than its worth.
> 
> We used to pass the index, and then use that index to lookup which
> target description to instantiate and use.  However, the above commit
> fixed an issue where we can't call malloc() within (certain parts of)
> the IPA (apparently), so instead we now pre-compute _every_ possible
> target description within the IPA.  The index is now only used to
> lookup which of the (many) pre-computed target descriptions to use.
> 
> It would (I think) have been easier all around if the IPA just
> self-inspected, figured out its own xcr0 value, and used that to
> create the one target description that is required.  So long as the
> xcr0 to target description code is shared (at compile time) with
> gdbserver, then we can be sure that the IPA will derive the same
> target description as gdbserver, and we would avoid all this index
> passing business, which has made this commit so very, very painful.
> 
> ** END OF AN ASIDE **
> 
> Currently then for x86/linux, gdbserver sends a number between 0 and 7
> to the IPA, and the IPA uses this to create a target description.
> 
> However, I am proposing that gdbserver should now create one of (up
> to) 64 different target descriptions for i386, so this 0 to 7 index
> isn't going to be good enough any more (amd64 and x32 have slightly
> fewer possible target descriptions, but still more than 8, so the
> problem is the same).
> 
> For a while I wondered if I was going to have to try and find some
> backward compatible solution for this mess.  But after seeing how
> lightly the IPA is actually documented, I wonder if it is not the case
> that there is a tight coupling between a version of gdbserver and a
> version of the IPA?  At least I'm hoping so.
> 
> In this commit I have thrown out the old IPA target description index
> numbering scheme, and switched to a completely new numbering scheme.
> Instead of the index that is passed being arbitrary, the index is
> instead calculated from the set of cpu features that are present on
> the target.  Within the IPA we can then reverse this logic to recreate
> the xcr0 value based on the index, and from the xcr0 value we can
> create the correct target description.
> 
> With the gdbserver to IPA numbering scheme issue resolved I have then
> update the gdbserver versions of amd64_linux_read_description and
> i386_linux_read_description so that they create target descriptions
> using the same set of cpu features as GDB itself.
> 
> After this gdbserver should now always come up with the same target
> description as GDB does on any x86/Linux target.
> 
> This commit does not introduce any new code sharing between GDB and
> gdbserver as previous commits in this series does.  Instead this
> commit is all about bringing GDB and gdbserver into alignment
> functionally so that the next commit can merge the GDB and gdbserver
> versions of these functions.

It does seem like the IPA case should just compute the tdesc it needs
if I understand you correctly.

BTW, on other arches like aarch64 and I believe risc-v, a flat array
is not used for the tdesc cache.  Now the cache is a std::unordered_map<>
and there is a 'struct foo_features' that has an operator== and
std::hash<> specialization.  It really would be nice if x86 was the same,
but that would require abandoning a notion of a portable "index" shared
between gdbserver and the IPA.

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 0/7] x86/Linux Target Description Changes
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
                     ` (6 preceding siblings ...)
  2024-03-05 17:00   ` [PATCHv2 7/7] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
@ 2024-03-19 16:05   ` John Baldwin
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
  8 siblings, 0 replies; 65+ messages in thread
From: John Baldwin @ 2024-03-19 16:05 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 3/5/24 9:00 AM, Andrew Burgess wrote:
> In v2:
> 
>    - Rebase to current upstream/master, no merge conflicts,
> 
>    - Retested.

This series all seems fine to me.  I did have one comment about
what I think would be ideal for x86 tdesc caching in the future
in reply to one of the patches.

Approved-By: John Baldwin <jhb@FreeBSD.org>

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/7] gdbserver: update target description creation for x86/linux
  2024-03-19 16:01     ` John Baldwin
@ 2024-03-19 18:34       ` Andrew Burgess
  2024-03-21 17:28         ` John Baldwin
  0 siblings, 1 reply; 65+ messages in thread
From: Andrew Burgess @ 2024-03-19 18:34 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

John Baldwin <jhb@FreeBSD.org> writes:

> On 3/5/24 9:00 AM, Andrew Burgess wrote:
>> This commit is part of a series which aims to share more of the target
>> description creation between GDB and gdbserver for x86/Linux.
>> 
>> After some refactoring, the previous commit actually started to share
>> some code, we added the shared x86_linux_tdesc_for_tid function into
>> nat/x86-linux-tdesc.c.  However, this function still relies on
>> amd64_linux_read_description and i386_linux_read_description which are
>> implemented separately for both gdbserver and GDB.  Given that at
>> their core, all these functions to is:
>> 
>>    1. take an xcr0 value as input,
>>    2. mask out some feature bits,
>>    3. look for a cached pre-generated target description and return it
>>       if found,
>>    4. if no cached target description is found then call either
>>       amd64_create_target_description or
>>       i386_create_target_description to create a new target
>>       description, which is then added to the cache.  Return the newly
>>       created target description.
>> 
>> The inner functions amd64_create_target_description and
>> i386_create_target_description are already shared between GDB and
>> gdbserver (in the gdb/arch/ directory), so the only thing that
>> the *_read_description functions really do is add the caching layer,
>> and it feels like this really could be shared.
>> 
>> However, we have a small problem.
>> 
>> On the GDB side we create target descriptions using a different set of
>> cpu features than on the gdbserver side!  This means that for the
>> exact same target, we might get a different target description when
>> using native GDB vs using gdbserver.  This surely feels like a
>> mistake, I would expect to get the same target description on each.
>> 
>> The table below shows the number of possible different target
>> descriptions that we can create on the GDB side vs on the gdbserver
>> side for each target type:
>> 
>>          | GDB | gdbserver
>>    ------|-----|----------
>>    i386  | 64  | 7
>>    amd64 | 32  | 7
>>    x32   | 16  | 7
>> 
>> So in theory, all I want to do is move the GDB version
>> of *_read_description into the nat/ directory and have gdbserver use
>> that, then both GDB and gdbserver would be able to create any of the
>> possible target descriptions.
>> 
>> Unfortunately it's a little more complex than that due to the in
>> process agent (IPA).
>> 
>> When the IPA is in use, gdbserver sends a target description index to
>> the IPA, and the IPA uses this to find the correct target description
>> to use.
>> 
>> ** START OF AN ASIDE **
>> 
>> Back in the day I suspect this approach made perfect sense.  However
>> since this commit:
>> 
>>    commit a8806230241d201f808d856eaae4d44088117b0c
>>    Date:   Thu Dec 7 17:07:01 2017 +0000
>> 
>>        Initialize target description early in IPA
>> 
>> I think passing the index is now more trouble than its worth.
>> 
>> We used to pass the index, and then use that index to lookup which
>> target description to instantiate and use.  However, the above commit
>> fixed an issue where we can't call malloc() within (certain parts of)
>> the IPA (apparently), so instead we now pre-compute _every_ possible
>> target description within the IPA.  The index is now only used to
>> lookup which of the (many) pre-computed target descriptions to use.
>> 
>> It would (I think) have been easier all around if the IPA just
>> self-inspected, figured out its own xcr0 value, and used that to
>> create the one target description that is required.  So long as the
>> xcr0 to target description code is shared (at compile time) with
>> gdbserver, then we can be sure that the IPA will derive the same
>> target description as gdbserver, and we would avoid all this index
>> passing business, which has made this commit so very, very painful.
>> 
>> ** END OF AN ASIDE **
>> 
>> Currently then for x86/linux, gdbserver sends a number between 0 and 7
>> to the IPA, and the IPA uses this to create a target description.
>> 
>> However, I am proposing that gdbserver should now create one of (up
>> to) 64 different target descriptions for i386, so this 0 to 7 index
>> isn't going to be good enough any more (amd64 and x32 have slightly
>> fewer possible target descriptions, but still more than 8, so the
>> problem is the same).
>> 
>> For a while I wondered if I was going to have to try and find some
>> backward compatible solution for this mess.  But after seeing how
>> lightly the IPA is actually documented, I wonder if it is not the case
>> that there is a tight coupling between a version of gdbserver and a
>> version of the IPA?  At least I'm hoping so.
>> 
>> In this commit I have thrown out the old IPA target description index
>> numbering scheme, and switched to a completely new numbering scheme.
>> Instead of the index that is passed being arbitrary, the index is
>> instead calculated from the set of cpu features that are present on
>> the target.  Within the IPA we can then reverse this logic to recreate
>> the xcr0 value based on the index, and from the xcr0 value we can
>> create the correct target description.
>> 
>> With the gdbserver to IPA numbering scheme issue resolved I have then
>> update the gdbserver versions of amd64_linux_read_description and
>> i386_linux_read_description so that they create target descriptions
>> using the same set of cpu features as GDB itself.
>> 
>> After this gdbserver should now always come up with the same target
>> description as GDB does on any x86/Linux target.
>> 
>> This commit does not introduce any new code sharing between GDB and
>> gdbserver as previous commits in this series does.  Instead this
>> commit is all about bringing GDB and gdbserver into alignment
>> functionally so that the next commit can merge the GDB and gdbserver
>> versions of these functions.
>
> It does seem like the IPA case should just compute the tdesc it needs
> if I understand you correctly.

Yes, totally.  And it's not like it would even add much code to the IPA,
I believe it already pulls in all of the target description creation
code, the only thing that is "saved" is the code that probes the
hardware to figure out which description we should use.

This whole area seems like it has not aged well :-/

>
> BTW, on other arches like aarch64 and I believe risc-v, a flat array
> is not used for the tdesc cache.  Now the cache is a std::unordered_map<>
> and there is a 'struct foo_features' that has an operator== and
> std::hash<> specialization.  It really would be nice if x86 was the same,
> but that would require abandoning a notion of a portable "index" shared
> between gdbserver and the IPA.

Indeed.  I did the worked on the feature/hash/lookup for RISC-V so I had
this in mind when looking at this code.  Originally I had planned to
switch to a map style cache, but when I found the IPA/index passing code
I lost my nerve and tried to find a solution that was inline with what
we had currently.

I don't really know about the IPA enough to make claims about what
is/isn't a reasonable change in this area.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/7] gdbserver: update target description creation for x86/linux
  2024-03-19 18:34       ` Andrew Burgess
@ 2024-03-21 17:28         ` John Baldwin
  2024-03-26 10:01           ` Luis Machado
  0 siblings, 1 reply; 65+ messages in thread
From: John Baldwin @ 2024-03-21 17:28 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 3/19/24 11:34 AM, Andrew Burgess wrote:
> John Baldwin <jhb@FreeBSD.org> writes:
> 
>> On 3/5/24 9:00 AM, Andrew Burgess wrote:
>>> This commit is part of a series which aims to share more of the target
>>> description creation between GDB and gdbserver for x86/Linux.
>>>
>>> After some refactoring, the previous commit actually started to share
>>> some code, we added the shared x86_linux_tdesc_for_tid function into
>>> nat/x86-linux-tdesc.c.  However, this function still relies on
>>> amd64_linux_read_description and i386_linux_read_description which are
>>> implemented separately for both gdbserver and GDB.  Given that at
>>> their core, all these functions to is:
>>>
>>>     1. take an xcr0 value as input,
>>>     2. mask out some feature bits,
>>>     3. look for a cached pre-generated target description and return it
>>>        if found,
>>>     4. if no cached target description is found then call either
>>>        amd64_create_target_description or
>>>        i386_create_target_description to create a new target
>>>        description, which is then added to the cache.  Return the newly
>>>        created target description.
>>>
>>> The inner functions amd64_create_target_description and
>>> i386_create_target_description are already shared between GDB and
>>> gdbserver (in the gdb/arch/ directory), so the only thing that
>>> the *_read_description functions really do is add the caching layer,
>>> and it feels like this really could be shared.
>>>
>>> However, we have a small problem.
>>>
>>> On the GDB side we create target descriptions using a different set of
>>> cpu features than on the gdbserver side!  This means that for the
>>> exact same target, we might get a different target description when
>>> using native GDB vs using gdbserver.  This surely feels like a
>>> mistake, I would expect to get the same target description on each.
>>>
>>> The table below shows the number of possible different target
>>> descriptions that we can create on the GDB side vs on the gdbserver
>>> side for each target type:
>>>
>>>           | GDB | gdbserver
>>>     ------|-----|----------
>>>     i386  | 64  | 7
>>>     amd64 | 32  | 7
>>>     x32   | 16  | 7
>>>
>>> So in theory, all I want to do is move the GDB version
>>> of *_read_description into the nat/ directory and have gdbserver use
>>> that, then both GDB and gdbserver would be able to create any of the
>>> possible target descriptions.
>>>
>>> Unfortunately it's a little more complex than that due to the in
>>> process agent (IPA).
>>>
>>> When the IPA is in use, gdbserver sends a target description index to
>>> the IPA, and the IPA uses this to find the correct target description
>>> to use.
>>>
>>> ** START OF AN ASIDE **
>>>
>>> Back in the day I suspect this approach made perfect sense.  However
>>> since this commit:
>>>
>>>     commit a8806230241d201f808d856eaae4d44088117b0c
>>>     Date:   Thu Dec 7 17:07:01 2017 +0000
>>>
>>>         Initialize target description early in IPA
>>>
>>> I think passing the index is now more trouble than its worth.
>>>
>>> We used to pass the index, and then use that index to lookup which
>>> target description to instantiate and use.  However, the above commit
>>> fixed an issue where we can't call malloc() within (certain parts of)
>>> the IPA (apparently), so instead we now pre-compute _every_ possible
>>> target description within the IPA.  The index is now only used to
>>> lookup which of the (many) pre-computed target descriptions to use.
>>>
>>> It would (I think) have been easier all around if the IPA just
>>> self-inspected, figured out its own xcr0 value, and used that to
>>> create the one target description that is required.  So long as the
>>> xcr0 to target description code is shared (at compile time) with
>>> gdbserver, then we can be sure that the IPA will derive the same
>>> target description as gdbserver, and we would avoid all this index
>>> passing business, which has made this commit so very, very painful.
>>>
>>> ** END OF AN ASIDE **
>>>
>>> Currently then for x86/linux, gdbserver sends a number between 0 and 7
>>> to the IPA, and the IPA uses this to create a target description.
>>>
>>> However, I am proposing that gdbserver should now create one of (up
>>> to) 64 different target descriptions for i386, so this 0 to 7 index
>>> isn't going to be good enough any more (amd64 and x32 have slightly
>>> fewer possible target descriptions, but still more than 8, so the
>>> problem is the same).
>>>
>>> For a while I wondered if I was going to have to try and find some
>>> backward compatible solution for this mess.  But after seeing how
>>> lightly the IPA is actually documented, I wonder if it is not the case
>>> that there is a tight coupling between a version of gdbserver and a
>>> version of the IPA?  At least I'm hoping so.
>>>
>>> In this commit I have thrown out the old IPA target description index
>>> numbering scheme, and switched to a completely new numbering scheme.
>>> Instead of the index that is passed being arbitrary, the index is
>>> instead calculated from the set of cpu features that are present on
>>> the target.  Within the IPA we can then reverse this logic to recreate
>>> the xcr0 value based on the index, and from the xcr0 value we can
>>> create the correct target description.
>>>
>>> With the gdbserver to IPA numbering scheme issue resolved I have then
>>> update the gdbserver versions of amd64_linux_read_description and
>>> i386_linux_read_description so that they create target descriptions
>>> using the same set of cpu features as GDB itself.
>>>
>>> After this gdbserver should now always come up with the same target
>>> description as GDB does on any x86/Linux target.
>>>
>>> This commit does not introduce any new code sharing between GDB and
>>> gdbserver as previous commits in this series does.  Instead this
>>> commit is all about bringing GDB and gdbserver into alignment
>>> functionally so that the next commit can merge the GDB and gdbserver
>>> versions of these functions.
>>
>> It does seem like the IPA case should just compute the tdesc it needs
>> if I understand you correctly.
> 
> Yes, totally.  And it's not like it would even add much code to the IPA,
> I believe it already pulls in all of the target description creation
> code, the only thing that is "saved" is the code that probes the
> hardware to figure out which description we should use.
> 
> This whole area seems like it has not aged well :-/
> 
>>
>> BTW, on other arches like aarch64 and I believe risc-v, a flat array
>> is not used for the tdesc cache.  Now the cache is a std::unordered_map<>
>> and there is a 'struct foo_features' that has an operator== and
>> std::hash<> specialization.  It really would be nice if x86 was the same,
>> but that would require abandoning a notion of a portable "index" shared
>> between gdbserver and the IPA.
> 
> Indeed.  I did the worked on the feature/hash/lookup for RISC-V so I had
> this in mind when looking at this code.  Originally I had planned to
> switch to a map style cache, but when I found the IPA/index passing code
> I lost my nerve and tried to find a solution that was inline with what
> we had currently.
> 
> I don't really know about the IPA enough to make claims about what
> is/isn't a reasonable change in this area.

I have zero knowledge about the IPA myself.  OTOH, if no one else steps
up with a strong opinion here, I would suggest that you go ahead and
fix the IPA as you described so it is more self-contained and then we can
use a saner approach for dealing with tdesc caching for x86.

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 0/8]  x86/Linux Target Description Changes
  2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
                     ` (7 preceding siblings ...)
  2024-03-19 16:05   ` [PATCHv2 0/7] x86/Linux Target Description Changes John Baldwin
@ 2024-03-23 16:35   ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 1/8] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
                       ` (9 more replies)
  8 siblings, 10 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools

In v3:

  - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
    I've resolved, but am unable to test.  Reposting so the author of
    that other commit can validate.

  - Initial testing looks good.  Full tests are still running.

In v2:

  - Rebase to current upstream/master, no merge conflicts,

  - Retested.

---

Andrew Burgess (8):
  gdbserver: convert have_ptrace_getregset to a tribool
  gdb/x86: move reading of cs and ds state into gdb/nat directory
  gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  gdb/gdbserver: share some code relating to target description creation
  gdb/arch: assert that X86_XSTATE_MPX is not set for x32
  gdbserver: update target description creation for x86/linux
  gdb/gdbserver: share x86/linux tdesc caching

 gdb/Makefile.in              |   1 +
 gdb/amd64-linux-tdep.c       |  33 +--
 gdb/amd64-linux-tdep.h       |   6 -
 gdb/arch/amd64.c             |   8 +-
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |  32 +--
 gdb/i386-linux-tdep.h        |  23 --
 gdb/nat/x86-linux-tdesc.c    | 411 +++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    | 115 ++++++++++
 gdb/nat/x86-linux.c          |  47 ++++
 gdb/nat/x86-linux.h          |  48 ++++
 gdb/x86-linux-nat.c          | 123 ++---------
 gdbserver/Makefile.in        |   4 +
 gdbserver/configure.srv      |   4 +
 gdbserver/linux-amd64-ipa.cc |  45 +---
 gdbserver/linux-arm-low.cc   |   6 +-
 gdbserver/linux-i386-ipa.cc  |  25 +--
 gdbserver/linux-low.cc       |   2 +-
 gdbserver/linux-low.h        |   2 +-
 gdbserver/linux-x86-low.cc   | 189 +++++-----------
 gdbserver/linux-x86-tdesc.cc | 141 +-----------
 gdbserver/linux-x86-tdesc.h  |  56 -----
 22 files changed, 749 insertions(+), 576 deletions(-)
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h
 delete mode 100644 gdbserver/linux-x86-tdesc.h


base-commit: e9315f148d56b3f4c7cfeef469633e85933d412c
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 1/8] gdbserver: convert have_ptrace_getregset to a tribool
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 2/8] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
                       ` (8 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

Convert the have_ptrace_getregset global within gdbserver to a
tribool.  This brings the flag into alignment with the corresponding
flag in GDB.

The gdbserver have_ptrace_getregset variable is already used as a
tribool, it just doesn't have the tribool type.

In a future commit I plan to share more code between GDB and
gdbserver, and having this variable be the same type in both code
bases will make the sharing much easier.

There should be no user visible changes after this commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-arm-low.cc |  6 +++---
 gdbserver/linux-low.cc     |  2 +-
 gdbserver/linux-low.h      |  2 +-
 gdbserver/linux-x86-low.cc | 10 +++++-----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 396ec88081b..b4f0e071c92 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -1007,9 +1007,9 @@ arm_target::low_arch_setup ()
 
   /* Check if PTRACE_GETREGSET works.  */
   if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
-    have_ptrace_getregset = 1;
+    have_ptrace_getregset = TRIBOOL_TRUE;
   else
-    have_ptrace_getregset = 0;
+    have_ptrace_getregset = TRIBOOL_FALSE;
 }
 
 bool
@@ -1122,7 +1122,7 @@ arm_target::get_regs_info ()
 {
   const struct target_desc *tdesc = current_process ()->tdesc;
 
-  if (have_ptrace_getregset == 1
+  if (have_ptrace_getregset == TRIBOOL_TRUE
       && (is_aarch32_linux_description (tdesc)
 	  || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
     return &regs_info_aarch32;
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 9d5a6242803..dfa26d451b0 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -135,7 +135,7 @@ typedef struct
 #endif
 
 /* Does the current host support PTRACE_GETREGSET?  */
-int have_ptrace_getregset = -1;
+enum tribool have_ptrace_getregset = TRIBOOL_UNKNOWN;
 
 /* Return TRUE if THREAD is the leader thread of the process.  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index d34d2738238..eaf87527338 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -951,7 +951,7 @@ void thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid);
 
 bool thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len);
 
-extern int have_ptrace_getregset;
+extern enum tribool have_ptrace_getregset;
 
 /* Search for the value with type MATCH in the auxv vector, with entries of
    length WORDSIZE bytes, of process with pid PID.  If found, store the
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 933d1fb012a..04202e355bb 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -899,7 +899,7 @@ x86_linux_read_description (void)
       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
 	{
 	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = 0;
+	  have_ptrace_getregset = TRIBOOL_FALSE;
 	  return i386_linux_read_description (X86_XSTATE_X87);
 	}
       else
@@ -918,7 +918,7 @@ x86_linux_read_description (void)
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-  if (have_ptrace_getregset == -1)
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
       struct iovec iov;
@@ -929,10 +929,10 @@ x86_linux_read_description (void)
       /* Check if PTRACE_GETREGSET works.  */
       if (ptrace (PTRACE_GETREGSET, tid,
 		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = 0;
+	have_ptrace_getregset = TRIBOOL_FALSE;
       else
 	{
-	  have_ptrace_getregset = 1;
+	  have_ptrace_getregset = TRIBOOL_TRUE;
 
 	  /* Get XCR0 from XSAVE extended state.  */
 	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
@@ -955,7 +955,7 @@ x86_linux_read_description (void)
     }
 
   /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset
+  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
 		   && (xcr0 & X86_XSTATE_ALL_MASK));
 
   if (xcr0_features)
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 2/8] gdb/x86: move reading of cs and ds state into gdb/nat directory
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 1/8] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 3/8] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
                       ` (7 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

This patch is part of a series that has the aim of making the code
that, for x86, reads the target description for a native process
shared between GDB and gdbserver.

Within GDB part of this process involves reading the cs and ds state
from the 'struct user_regs_struct' using a ptrace call.

This isn't done by gdbserver, which is part of the motivation for this
whole series; the approach gdbserver takes is inferior to the approach
GDB takes.

This commit moves the reading of cs and ds, which is used to figure
out if a thread is 32-bit or 64-bit (or in x32 mode), into the gdb/nat
directory so that the code could be shared with gdbserver, but at this
point I'm not actually using the code in gdbserver, that will come
later.

As such there should be no user visible changes after this commit, GDB
continues to do things as it did before (reading cs/ds), while
gdbserver continues to use its own approach (which doesn't require
reading cs/ds).

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/nat/x86-linux.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux.h | 28 +++++++++++++++++++++++++++
 gdb/x86-linux-nat.c | 42 +++++-----------------------------------
 3 files changed, 80 insertions(+), 37 deletions(-)

diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index e61f4d749ba..4242a1baafb 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -20,6 +20,8 @@
 #include "gdbsupport/common-defs.h"
 #include "x86-linux.h"
 #include "x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include <sys/user.h>
 
 /* Per-thread arch-specific data we want to keep.  */
 
@@ -80,3 +82,48 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp)
 {
   x86_linux_update_debug_registers (lwp);
 }
+
+#ifdef __x86_64__
+/* Value of CS segment register:
+     64bit process: 0x33
+     32bit process: 0x23  */
+#define AMD64_LINUX_USER64_CS 0x33
+
+/* Value of DS segment register:
+     LP64 process: 0x0
+     X32 process: 0x2b  */
+#define AMD64_LINUX_X32_DS 0x2b
+#endif
+
+/* See nat/x86-linux.h.  */
+
+x86_linux_arch_size
+x86_linux_ptrace_get_arch_size (int tid)
+{
+#ifdef __x86_64__
+  unsigned long cs;
+  unsigned long ds;
+
+  /* Get CS register.  */
+  errno = 0;
+  cs = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, cs), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get CS register"));
+
+  bool is_64bit = cs == AMD64_LINUX_USER64_CS;
+
+  /* Get DS register.  */
+  errno = 0;
+  ds = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, ds), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get DS register"));
+
+  bool is_x32 = ds == AMD64_LINUX_X32_DS;
+
+  return x86_linux_arch_size (is_64bit, is_x32);
+#else
+  return x86_linux_arch_size (false, false);
+#endif
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 822882173f9..15153ea277e 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -47,4 +47,32 @@ extern void x86_linux_delete_thread (struct arch_lwp_info *arch_lwp);
 
 extern void x86_linux_prepare_to_resume (struct lwp_info *lwp);
 
+/* Return value from x86_linux_ptrace_get_arch_size function.  Indicates if
+   a thread is 32-bit, 64-bit, or x32.  */
+
+struct x86_linux_arch_size
+{
+  explicit x86_linux_arch_size (bool is_64bit, bool is_x32)
+    : m_is_64bit (is_64bit),
+      m_is_x32 (is_x32)
+  {
+    /* Nothing.  */
+  }
+
+  bool is_64bit () const
+  { return m_is_64bit; }
+
+  bool is_x32 () const
+  { return m_is_x32; }
+
+private:
+  bool m_is_64bit = false;
+  bool m_is_x32 = false;
+};
+
+/* Use ptrace calls to figure out if thread TID is 32-bit, 64-bit, or
+   64-bit running in x32 mode.  */
+
+extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
+
 #endif /* NAT_X86_LINUX_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b93ffca38db..b39d05c401f 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -91,18 +91,6 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
   linux_nat_target::post_startup_inferior (ptid);
 }
 
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
 /* Get Linux/x86 target description from running target.  */
 
 const struct target_desc *
@@ -122,31 +110,11 @@ x86_linux_nat_target::read_description ()
   tid = inferior_ptid.pid ();
 
 #ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  is_64bit = arch_size.is_64bit ();
+  is_x32 = arch_size.is_x32 ();
+
 #elif HAVE_PTRACE_GETFPXREGS
   if (have_ptrace_getfpxregs == -1)
     {
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 3/8] gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 1/8] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 2/8] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 4/8] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
                       ` (6 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

This commit is part of a series that aims to share more of the x86
target description reading/generation code between GDB and gdbserver.

There are a huge number of similarities between the code in
gdbserver's x86_linux_read_description function and GDB's
x86_linux_nat_target::read_description function, and it is this
similarity that I plan, in a later commit, to share between GDB and
gdbserver.

However, one thing that is different in x86_linux_read_description is
the code inside the '!use_xml' block.  This is the code that handles
the case where gdbserver is not allowed to send an XML target
description back to GDB.  In this case gdbserver uses some predefined,
fixed, target descriptions.

First, it's worth noting that I suspect this code is not tested any
more.  I couldn't find anything in the testsuite that tries to disable
XML target description support.  And the idea of having a single
"fixed" target description really doesn't work well when we think
about all the various x86 extensions that exist.  Part of me would
like to rip out the no-xml support in gdbserver (at least for x86),
and if a GDB connects that doesn't support XML target descriptions,
gdbserver can just give an error and drop the connection.  GDB has
supported XML target descriptions for 16 years now, I think it would
be reasonable for our shipped gdbserver to drop support for the old
way of doing things.

Anyway.... this commit doesn't do that.

What I did notice was that, over time, the '!use_xml' block appears to
have "drifted" within the x86_linux_read_description function; it's
now not the first check we do.  Instead we make some ptrace calls and
return a target description generated based on the result of these
ptrace calls.  Surely it only makes sense to generate variable target
descriptions if we can send these back to GDB?

So in this commit I propose to move the '!use_xml' block earlier in
the x86_linux_read_description function.

The benefit of this is that this leaves the later half of
x86_linux_read_description much more similar to the GDB function
x86_linux_nat_target::read_description and sets us up for potentially
sharing code between GDB and gdbserver in a later commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-x86-low.cc | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 04202e355bb..872c3fc69c7 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -891,6 +891,22 @@ x86_linux_read_description (void)
 #endif
     }
 
+  /* If we are not allowed to send an XML target description then we need
+     to use the hard-wired target descriptions.  This corresponds to GDB's
+     default machine for x86.
+
+     This check needs to occur before any returns statements that might
+     generate some alternative target descriptions.  */
+  if (!use_xml)
+    {
+#ifdef __x86_64__
+      if (machine == EM_X86_64)
+	return tdesc_amd64_linux_no_xml.get ();
+      else
+#endif
+	return tdesc_i386_linux_no_xml.get ();
+    }
+
 #if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
   if (machine == EM_386 && have_ptrace_getfpxregs == -1)
     {
@@ -907,17 +923,6 @@ x86_linux_read_description (void)
     }
 #endif
 
-  if (!use_xml)
-    {
-      /* Don't use XML.  */
-#ifdef __x86_64__
-      if (machine == EM_X86_64)
-	return tdesc_amd64_linux_no_xml.get ();
-      else
-#endif
-	return tdesc_i386_linux_no_xml.get ();
-    }
-
   if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 4/8] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (2 preceding siblings ...)
  2024-03-23 16:35     ` [PATCHv3 3/8] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 5/8] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
                       ` (5 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

Share the definition of I386_LINUX_XSAVE_XCR0_OFFSET between GDB and
gdbserver.

This commit is part of a series that aims to share more of the x86
target description creation code between GDB and gdbserver.  The
I386_LINUX_XSAVE_XCR0_OFFSET #define is used as part of the target
description creation, and I noticed that this constant is defined
separately for GDB and gdbserver.

This commit moves the definition into gdb/nat/x86-linux.h, which
allows the #define to be shared.

There should be no user visible changes after this commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/amd64-linux-tdep.c     |  1 +
 gdb/i386-linux-tdep.c      |  1 +
 gdb/i386-linux-tdep.h      | 20 --------------------
 gdb/nat/x86-linux.h        | 20 ++++++++++++++++++++
 gdbserver/linux-x86-low.cc | 22 +---------------------
 5 files changed, 23 insertions(+), 41 deletions(-)

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 1deb13b4e9a..a512ec5dd02 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -42,6 +42,7 @@
 #include "arch/amd64.h"
 #include "target-descriptions.h"
 #include "expop.h"
+#include "nat/x86-linux.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index a2f937690c2..8dd7203b6c3 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -40,6 +40,7 @@
 
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 5891747572b..07593c6a8ec 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -58,26 +58,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 /* Return the target description according to XCR0.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */ 
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 15153ea277e..855a8d14f91 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -22,6 +22,26 @@
 
 #include "nat/linux-nat.h"
 
+/* Format of XSAVE extended state is:
+	struct
+	{
+	  fxsave_bytes[0..463]
+	  sw_usable_bytes[464..511]
+	  xstate_hdr_bytes[512..575]
+	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
+	};
+
+  Same memory layout will be used for the coredump NT_X86_XSTATE
+  representing the XSAVE extended state registers.
+
+  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+  extended state mask, which is the same as the extended control register
+  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
+  together with the mask saved in the xstate_hdr_bytes to determine what
+  states the processor/OS supports and what state, used or initialized,
+  the process/thread is in.  */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
 /* Set whether our local mirror of LWP's debug registers has been
    changed since the values were last written to the thread.  Nonzero
    indicates that a change has been made, zero indicates no change.  */
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 872c3fc69c7..30d876efc5d 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -27,6 +27,7 @@
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-xstate.h"
 #include "nat/gdb_ptrace.h"
+#include "nat/x86-linux.h"
 
 #ifdef __x86_64__
 #include "nat/amd64-linux-siginfo.h"
@@ -832,27 +833,6 @@ x86_target::low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
 \f
 static int use_xml;
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  avx_bytes[576..831]
-	  future_state etc
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 /* Does the current host support the GETFPXREGS request?  The header
    file may or may not define it, and even if it is defined, the
    kernel will return EIO if it's running on a pre-SSE processor.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 5/8] gdb/gdbserver: share some code relating to target description creation
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (3 preceding siblings ...)
  2024-03-23 16:35     ` [PATCHv3 4/8] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 6/8] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
                       ` (4 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

This commit is part of a series to share more of the x86 target
description creation code between GDB and gdbserver.

Unlike previous commits which were mostly refactoring, this commit is
the first that makes a real change, though that change should mostly
be for gdbserver; I've largely adopted the "GDB" way of doing things
for gdbserver, and this fixes a real gdbserver bug.

On a x86-64 Linux target, running the test:

  gdb.server/connect-with-no-symbol-file.exp

results in two core files being created.  Both of these core files are
from the inferior process, created after gdbserver has detached.

In this test a gdbserver process is started and then, after gdbserver
has started, but before GDB attaches, we either delete the inferior
executable, or change its permissions so it can't be read.  Only after
doing this do we attempt to connect with GDB.

As GDB connects to gdbserver, gdbserver attempts to figure out the
target description so that it can send the description to GDB, this
involves a call to x86_linux_read_description.

In x86_linux_read_description one of the first things we do is try to
figure out if the process is 32-bit or 64-bit.  To do this we look up
the executable via the thread-id, and then attempt to read the
architecture size from the executable.  This isn't going to work if
the executable has been deleted, or is no longer readable.

And so, as we can't read the executable, we default to an i386 target
and use an i386 target description.

A consequence of using an i386 target description is that addresses
are assumed to be 32-bits.  Here's an example session that shows the
problems this causes.  This is run on an x86-64 machine, and the test
binary (xx.x) is a standard 64-bit x86-64 binary:

  shell_1$ gdbserver --once localhost :54321 /tmp/xx.x

  shell_2$ gdb -q
  (gdb) set sysroot
  (gdb) shell chmod 000 /tmp/xx.x
  (gdb) target remote :54321
  Remote debugging using :54321
  warning: /tmp/xx.x: Permission denied.
  0xf7fd3110 in ?? ()
  (gdb) show architecture
  The target architecture is set to "auto" (currently "i386").
  (gdb) p/x $pc
  $1 = 0xf7fd3110
  (gdb) info proc mappings
  process 2412639
  Mapped address spaces:

  	Start Addr   End Addr       Size     Offset  Perms   objfile
  	  0x400000   0x401000     0x1000        0x0  r--p   /tmp/xx.x
  	  0x401000   0x402000     0x1000     0x1000  r-xp   /tmp/xx.x
  	  0x402000   0x403000     0x1000     0x2000  r--p   /tmp/xx.x
  	  0x403000   0x405000     0x2000     0x2000  rw-p   /tmp/xx.x
  	0xf7fcb000 0xf7fcf000     0x4000        0x0  r--p   [vvar]
  	0xf7fcf000 0xf7fd1000     0x2000        0x0  r-xp   [vdso]
  	0xf7fd1000 0xf7fd3000     0x2000        0x0  r--p   /usr/lib64/ld-2.30.so
  	0xf7fd3000 0xf7ff3000    0x20000     0x2000  r-xp   /usr/lib64/ld-2.30.so
  	0xf7ff3000 0xf7ffb000     0x8000    0x22000  r--p   /usr/lib64/ld-2.30.so
  	0xf7ffc000 0xf7ffe000     0x2000    0x2a000  rw-p   /usr/lib64/ld-2.30.so
  	0xf7ffe000 0xf7fff000     0x1000        0x0  rw-p
  	0xfffda000 0xfffff000    0x25000        0x0  rw-p   [stack]
  	0xff600000 0xff601000     0x1000        0x0  r-xp   [vsyscall]
  (gdb) info inferiors
    Num  Description       Connection           Executable
  * 1    process 2412639   1 (remote :54321)
  (gdb) shell cat /proc/2412639/maps
  00400000-00401000 r--p 00000000 fd:03 45907133           /tmp/xx.x
  00401000-00402000 r-xp 00001000 fd:03 45907133           /tmp/xx.x
  00402000-00403000 r--p 00002000 fd:03 45907133           /tmp/xx.x
  00403000-00405000 rw-p 00002000 fd:03 45907133           /tmp/xx.x
  7ffff7fcb000-7ffff7fcf000 r--p 00000000 00:00 0          [vvar]
  7ffff7fcf000-7ffff7fd1000 r-xp 00000000 00:00 0          [vdso]
  7ffff7fd1000-7ffff7fd3000 r--p 00000000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7fd3000-7ffff7ff3000 r-xp 00002000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ff3000-7ffff7ffb000 r--p 00022000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffc000-7ffff7ffe000 rw-p 0002a000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  7ffffffda000-7ffffffff000 rw-p 00000000 00:00 0          [stack]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]
  (gdb)

Notice the difference between the mappings reported via GDB and those
reported directly from the kernel via /proc/PID/maps, the addresses of
every mapping is clamped to 32-bits for GDB, while the kernel reports
real 64-bit addresses.

Notice also that the $pc value is a 32-bit value.  It appears to be
within one of the mappings reported by GDB, but is outside any of the
mappings reported from the kernel.

And this is where the problem arises.  When gdbserver detaches from
the inferior we pass the inferior the address from which it should
resume.  Due to the 32/64 bit confusion we tell the inferior to resume
from the 32-bit $pc value, which is not within any valid mapping, and
so, as soon as the inferior resumes, it segfaults.

If we look at how GDB (not gdbserver) figures out its target
description then we see an interesting difference.  GDB doesn't try to
read the executable.  Instead GDB uses ptrace to query the thread's
state, and uses this to figure out the if the thread is 32 or 64 bit.

If we update gdbserver to do it the "GDB" way then the above problem
is resolved, gdbserver now sees the process as 64-bit, and when we
detach from the inferior we give it the correct 64-bit address, and
the inferior no longer segfaults.

Now, I could just update the gdbserver code, but better, I think, to
share one copy of the code between GDB and gdbserver in gdb/nat/.
That is what this commit does.

The cores of x86_linux_read_description from gdbserver and
x86_linux_nat_target::read_description from GDB are moved into a new
file gdb/nat/x86-linux-tdesc.c and combined into a single function
x86_linux_tdesc_for_tid which is called from each location.

This new function does things the GDB way, the only changes are to
allow for the sharing; we now have a callback function to call the
first time that the xcr0 state is read, this allows for GDB and
gdbserver to perform their own initialisation as needed, and
additionally, the new function takes a pointer for where to cache the
xcr0 value, this isn't needed for this commit, but will be useful in a
later commit where gdbserver will want to read this cached xcr0
value.

Another thing to note about this commit is how the functions
i386_linux_read_description and amd64_linux_read_description are
handled.  For now I've left these function as implemented separately
in GDB and gdbserver.  I've moved the declarations of these functions
into gdb/nat/x86-linux-tdesc.h, but the implementations are left as
separate.

A later commit in this series will make these functions shared too,
but doing this is not trivial, so I've left that for a separate
commit.  Merging the declarations as I've done here ensures that
everyone implements the function to the same API, and once these
functions are shared (in a later commit) we'll want a shared
declaration anyway.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/Makefile.in              |   1 +
 gdb/amd64-linux-tdep.c       |   1 +
 gdb/amd64-linux-tdep.h       |   6 --
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |   1 +
 gdb/i386-linux-tdep.h        |   3 -
 gdb/nat/x86-linux-tdesc.c    | 124 +++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  75 ++++++++++++++++++
 gdb/x86-linux-nat.c          |  91 ++++-----------------
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 +
 gdbserver/linux-i386-ipa.cc  |   1 +
 gdbserver/linux-x86-low.cc   | 148 +++++++++++------------------------
 gdbserver/linux-x86-tdesc.cc |   1 +
 gdbserver/linux-x86-tdesc.h  |   7 --
 15 files changed, 267 insertions(+), 199 deletions(-)
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 331620375ae..38f4d5fde98 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1585,6 +1585,7 @@ HFILES_NO_SRCDIR = \
 	nat/x86-gcc-cpuid.h \
 	nat/x86-linux.h \
 	nat/x86-linux-dregs.h \
+	nat/x86-linux-tdesc.h \
 	python/py-event.h \
 	python/py-events.h \
 	python/py-stopevent.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index a512ec5dd02..7e0900dc6f9 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -43,6 +43,7 @@
 #include "target-descriptions.h"
 #include "expop.h"
 #include "nat/x86-linux.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h
index 2003dcda78f..0ec49e7fe03 100644
--- a/gdb/amd64-linux-tdep.h
+++ b/gdb/amd64-linux-tdep.h
@@ -43,12 +43,6 @@ extern struct target_desc *tdesc_x32_linux;
 extern struct target_desc *tdesc_x32_avx_linux;
 extern struct target_desc *tdesc_x32_avx_avx512_linux;
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  */
-
-const target_desc *amd64_linux_read_description (uint64_t xcr0_features_bit,
-						 bool is_x32);
-
 /* Enum that defines the syscall identifiers for amd64 linux.
    Used for process record/replay, these will be translated into
    a gdb-canonical set of syscall ids in linux-record.c.  */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 8b98511cef7..4bcc0696027 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -322,7 +322,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o \
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
 		nat/amd64-linux-siginfo.o"
 		;;
 	    sparc)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 8dd7203b6c3..f5f7a36bf36 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -41,6 +41,7 @@
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 07593c6a8ec..e8691cd778e 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -55,9 +55,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 					   struct ui_out *uiout,
 					   enum gdb_signal siggnal);
 
-/* Return the target description according to XCR0.  */
-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
new file mode 100644
index 00000000000..be7014d2b5f
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -0,0 +1,124 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "nat/x86-linux-tdesc.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#endif
+#include "arch/i386.h"
+
+#include "gdbsupport/common-defs.h"
+#include "nat/x86-linux.h"
+#include "nat/x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <linux/uio.h>
+#include <elf.h>
+#include <sys/user.h>
+#include <sys/user.h>
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage)
+{
+#ifdef __x86_64__
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  bool is_64bit = arch_size.is_64bit ();
+  bool is_x32 = arch_size.is_x32 ();
+
+  if (sizeof (void *) == 4 && is_64bit && !is_x32)
+    error ("%s", error_msg);
+
+#elif HAVE_PTRACE_GETFPXREGS
+  if (have_ptrace_getfpxregs == -1)
+    {
+      elf_fpxregset_t fpxregs;
+
+      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+	{
+	  have_ptrace_getfpxregs = 0;
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
+	}
+    }
+#endif
+
+  if (*have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    {
+      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, tid,
+		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
+	{
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  *xcr0_storage = 0;
+	}
+      else
+	{
+	  *have_ptrace_getregset = TRIBOOL_TRUE;
+
+	  /* Get XCR0 from XSAVE extended state.  */
+	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+				      / sizeof (uint64_t))];
+
+#ifdef __x86_64__
+	  /* No MPX on x32.  */
+	  if (is_64bit && is_x32)
+	    *xcr0_storage &= ~X86_XSTATE_MPX;
+#endif /* __x86_64__ */
+
+	  xcr0_init_cb (*xcr0_storage);
+	}
+    }
+
+  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
+     PTRACE_GETREGSET is not available then set xcr0_features_bits to
+     zero so that the "no-features" descriptions are returned by the
+     switches below.  */
+  uint64_t xcr0_features_bits;
+  if (*have_ptrace_getregset == TRIBOOL_TRUE)
+    xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+  else
+    xcr0_features_bits = 0;
+
+#ifdef __x86_64__
+  if (is_64bit)
+    {
+      return amd64_linux_read_description (xcr0_features_bits, is_x32);
+    }
+  else
+#endif
+    return i386_linux_read_description (xcr0_features_bits);
+
+  gdb_assert_not_reached ("failed to return tdesc");
+}
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
new file mode 100644
index 00000000000..3727a8bf95e
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -0,0 +1,75 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_X86_LINUX_TDESC_H
+#define NAT_X86_LINUX_TDESC_H
+
+#include "gdbsupport/function-view.h"
+
+struct target_desc;
+
+/* Return the target description for Linux thread TID.
+
+   When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
+   xcr0 is read using ptrace calls and stored into *XCR0_STORAGE.  Then
+   XCR0_INIT_CB is called with the value of *XCR0_STORAGE and
+   *HAVE_PTRACE_GETREGSET is set to TRIBOOL_TRUE.
+
+   If the attempt to read xcr0 using ptrace fails then *XCR0_STORAGE is set
+   to zero and *HAVE_PTRACE_GETREGSET is set to TRIBOOL_FALSE.
+
+   The storage pointed to by XCR0_STORAGE must exist until the program
+   terminates, this storage is used to cache the xcr0 value.  As such
+   XCR0_INIT_CB will only be called once if xcr0 is successfully read using
+   ptrace, or not at all if the ptrace call fails.
+
+   This function returns a target description based on the extracted xcr0
+   value along with other characteristics of the thread identified by TID.
+
+   This function can return nullptr if we encounter a machine configuration
+   for which a target_desc cannot be created.  Ideally this would not be
+   the case, we should be able to create a target description for every
+   possible machine configuration.  See amd64_linux_read_description and
+   i386_linux_read_description for cases when nullptr might be
+   returned.
+
+   ERROR_MSG is using in an error() call if we try to create a target
+   description for a 64-bit process but this is a 32-bit build of GDB.  */
+
+extern const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage);
+
+#ifdef __x86_64__
+
+/* Return the right amd64-linux target descriptions according to
+   XCR0_FEATURES_BIT and IS_X32.  This is implemented separately in both
+   GDB and gdbserver.  */
+
+extern const target_desc *amd64_linux_read_description
+	(uint64_t xcr0_features_bit, bool is_x32);
+
+#endif
+
+/* Return the target description according to XCR0.  This is implemented
+   separately in both GDB and gdbserver.  */
+extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+
+#endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index b39d05c401f..872e27e739f 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -42,6 +42,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "nat/linux-ptrace.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* linux_nat_target::low_new_fork implementation.  */
 
@@ -96,90 +97,26 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
 const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
+  static uint64_t xcr0_storage;
 
   if (inferior_ptid == null_ptid)
     return this->beneath ()->read_description ();
 
-  tid = inferior_ptid.pid ();
-
-#ifdef __x86_64__
-
-  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
-  is_64bit = arch_size.is_64bit ();
-  is_x32 = arch_size.is_x32 ();
-
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
-	}
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  m_xsave_layout = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset == TRIBOOL_TRUE)
-    xcr0_features_bits = xcr0 & X86_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      return amd64_linux_read_description (xcr0_features_bits, is_x32);
-#endif
-    }
-  else
-    {
-      const struct target_desc * tdesc
-	= i386_linux_read_description (xcr0_features_bits);
+  int tid = inferior_ptid.pid ();
 
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE_MASK);
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDB");
 
-      return tdesc;
-    }
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [&] (uint64_t xcr0)
+  {
+    this->m_xsave_layout
+      = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+  };
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb,
+				  error_msg, &xcr0_storage);
 }
 \f
 
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 9e861a75088..7a2702d78bf 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -109,6 +109,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -371,6 +372,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 54e4c9812bb..f97b0d6a1d9 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -23,6 +23,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* Defined in auto-generated file amd64-linux.c.  */
 void init_registers_amd64_linux (void);
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 2e4646f8c03..459b8055b5c 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -23,6 +23,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 30d876efc5d..9bf369f8a34 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -48,6 +48,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "linux-x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
 static target_desc_up tdesc_amd64_linux_no_xml;
@@ -844,32 +845,20 @@ int have_ptrace_getfpxregs =
 #endif
 ;
 
+/* Cached xcr0 value.  This is initialised the first time
+   x86_linux_read_description is called.  */
+
+static uint64_t xcr0_storage;
+
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
 x86_linux_read_description (void)
 {
-  unsigned int machine;
-  int is_elf64;
-  int xcr0_features;
-  int tid;
-  static uint64_t xcr0;
-  static int xsave_len;
-  struct regset_info *regset;
-
-  tid = lwpid_of (current_thread);
-
-  is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+  int tid = lwpid_of (current_thread);
 
-  if (sizeof (void *) == 4)
-    {
-      if (is_elf64 > 0)
-       error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
-      else if (machine == EM_X86_64)
-       error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
-    }
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDBserver");
 
   /* If we are not allowed to send an XML target description then we need
      to use the hard-wired target descriptions.  This corresponds to GDB's
@@ -879,103 +868,54 @@ x86_linux_read_description (void)
      generate some alternative target descriptions.  */
   if (!use_xml)
     {
+      x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+      bool is_64bit = arch_size.is_64bit ();
+      bool is_x32 = arch_size.is_x32 ();
+
+      if (sizeof (void *) == 4 && is_64bit && !is_x32)
+	error ("%s", error_msg);
+
 #ifdef __x86_64__
-      if (machine == EM_X86_64)
+      if (is_64bit && !is_x32)
 	return tdesc_amd64_linux_no_xml.get ();
       else
 #endif
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
-  if (machine == EM_386 && have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87);
-	}
-      else
-	have_ptrace_getfpxregs = 1;
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  /* No MPX on x32.  */
-	  if (machine == EM_X86_64 && !is_elf64)
-	    xcr0 &= ~X86_XSTATE_MPX;
-
-	  xsave_len = x86_xsave_length ();
-
-	  /* Use PTRACE_GETREGSET if it is available.  */
-	  for (regset = x86_regsets;
-	       regset->fill_function != NULL; regset++)
-	    if (regset->get_request == PTRACE_GETREGSET)
-	      regset->size = xsave_len;
-	    else if (regset->type != GENERAL_REGS)
-	      regset->size = 0;
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
-		   && (xcr0 & X86_XSTATE_ALL_MASK));
-
-  if (xcr0_features)
-    i387_set_xsave_mask (xcr0, xsave_len);
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [] (uint64_t xcr0)
+  {
+    i387_set_xsave_mask (xcr0, x86_xsave_length ());
+  };
 
-  if (machine == EM_X86_64)
-    {
-#ifdef __x86_64__
-      const target_desc *tdesc = NULL;
+  /* If have_ptrace_getregset is changed to true by calling
+     x86_linux_tdesc_for_tid then we will perform some additional
+     initialisation.  */
+  bool have_ptrace_getregset_is_unknown
+    = have_ptrace_getregset == TRIBOOL_UNKNOWN;
 
-      if (xcr0_features)
-	{
-	  tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
-						!is_elf64);
-	}
+  const target_desc *tdesc
+    = x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb, error_msg,
+			       &xcr0_storage);
 
-      if (tdesc == NULL)
-	tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
-      return tdesc;
-#endif
-    }
-  else
+  if (have_ptrace_getregset_is_unknown
+      && have_ptrace_getregset == TRIBOOL_TRUE)
     {
-      const target_desc *tdesc = NULL;
-
-      if (xcr0_features)
-	  tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
-
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE);
-
-      return tdesc;
+      int xsave_len = x86_xsave_length ();
+
+      /* Use PTRACE_GETREGSET if it is available.  */
+      for (regset_info *regset = x86_regsets;
+	   regset->fill_function != nullptr;
+	   regset++)
+	if (regset->get_request == PTRACE_GETREGSET)
+	  regset->size = xsave_len;
+	else if (regset->type != GENERAL_REGS)
+	  regset->size = 0;
     }
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return tdesc;
 }
 
 /* Update all the target description of all processes; a new GDB
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 626207fc477..9fd64d8574b 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -26,6 +26,7 @@
 #include "arch/amd64.h"
 #endif
 #include "x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* Return the right x86_linux_tdesc index for a given XCR0.  Return
    X86_TDESC_LAST if can't find a match.  */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index f9561b129ae..576aaf5e165 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -46,11 +46,4 @@ int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
 
 const struct target_desc *i386_get_ipa_tdesc (int idx);
 
-#ifdef __x86_64__
-const struct target_desc *amd64_linux_read_description (uint64_t xcr0,
-							bool is_x32);
-#endif
-
-const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 6/8] gdb/arch: assert that X86_XSTATE_MPX is not set for x32
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (4 preceding siblings ...)
  2024-03-23 16:35     ` [PATCHv3 5/8] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 7/8] gdbserver: update target description creation for x86/linux Andrew Burgess
                       ` (3 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools

While trying to merge this commit:

  commit 4bb20a6244b7091a9a7a2ae35dfbd7e8db27550a
  Date:   Wed Mar 20 04:13:18 2024 -0700

      gdbserver: Clear X86_XSTATE_MPX bits in xcr0 on x32

With this patch series of mine:

  https://inbox.sourceware.org/gdb-patches/cover.1706801009.git.aburgess@redhat.com

I worried that there could be other paths that could result in an xcr0
value that has X86_XSTATE_MPX set in x32 mode.  As everyone eventually
calls amd64_create_target_description to build their target
description, I figured we could assert in here that if X86_XSTATE_MPX
is set then we should not be an x32 target, this should uncover any
other bugs in this area.

I'm not currently able to build/run any x32 binaries, so I have no way
to test this.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31511
---
 gdb/arch/amd64.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index b8ff4270079..f182e686dda 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -66,8 +66,12 @@ amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
   if (xcr0 & X86_XSTATE_AVX)
     regnum = create_feature_i386_64bit_avx (tdesc.get (), regnum);
 
-  if ((xcr0 & X86_XSTATE_MPX) && !is_x32)
-    regnum = create_feature_i386_64bit_mpx (tdesc.get (), regnum);
+  if (xcr0 & X86_XSTATE_MPX)
+    {
+      /* MPX is not available on x32.  */
+      gdb_assert (!is_x32);
+      regnum = create_feature_i386_64bit_mpx (tdesc.get (), regnum);
+    }
 
   if (xcr0 & X86_XSTATE_AVX512)
     regnum = create_feature_i386_64bit_avx512 (tdesc.get (), regnum);
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 7/8] gdbserver: update target description creation for x86/linux
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (5 preceding siblings ...)
  2024-03-23 16:35     ` [PATCHv3 6/8] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-23 16:35     ` [PATCHv3 8/8] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
                       ` (2 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

This commit is part of a series which aims to share more of the target
description creation between GDB and gdbserver for x86/Linux.

After some refactoring, the previous commit actually started to share
some code, we added the shared x86_linux_tdesc_for_tid function into
nat/x86-linux-tdesc.c.  However, this function still relies on
amd64_linux_read_description and i386_linux_read_description which are
implemented separately for both gdbserver and GDB.  Given that at
their core, all these functions to is:

  1. take an xcr0 value as input,
  2. mask out some feature bits,
  3. look for a cached pre-generated target description and return it
     if found,
  4. if no cached target description is found then call either
     amd64_create_target_description or
     i386_create_target_description to create a new target
     description, which is then added to the cache.  Return the newly
     created target description.

The inner functions amd64_create_target_description and
i386_create_target_description are already shared between GDB and
gdbserver (in the gdb/arch/ directory), so the only thing that
the *_read_description functions really do is add the caching layer,
and it feels like this really could be shared.

However, we have a small problem.

On the GDB side we create target descriptions using a different set of
cpu features than on the gdbserver side!  This means that for the
exact same target, we might get a different target description when
using native GDB vs using gdbserver.  This surely feels like a
mistake, I would expect to get the same target description on each.

The table below shows the number of possible different target
descriptions that we can create on the GDB side vs on the gdbserver
side for each target type:

        | GDB | gdbserver
  ------|-----|----------
  i386  | 64  | 7
  amd64 | 32  | 7
  x32   | 16  | 7

So in theory, all I want to do is move the GDB version
of *_read_description into the nat/ directory and have gdbserver use
that, then both GDB and gdbserver would be able to create any of the
possible target descriptions.

Unfortunately it's a little more complex than that due to the in
process agent (IPA).

When the IPA is in use, gdbserver sends a target description index to
the IPA, and the IPA uses this to find the correct target description
to use.

** START OF AN ASIDE **

Back in the day I suspect this approach made perfect sense.  However
since this commit:

  commit a8806230241d201f808d856eaae4d44088117b0c
  Date:   Thu Dec 7 17:07:01 2017 +0000

      Initialize target description early in IPA

I think passing the index is now more trouble than its worth.

We used to pass the index, and then use that index to lookup which
target description to instantiate and use.  However, the above commit
fixed an issue where we can't call malloc() within (certain parts of)
the IPA (apparently), so instead we now pre-compute _every_ possible
target description within the IPA.  The index is now only used to
lookup which of the (many) pre-computed target descriptions to use.

It would (I think) have been easier all around if the IPA just
self-inspected, figured out its own xcr0 value, and used that to
create the one target description that is required.  So long as the
xcr0 to target description code is shared (at compile time) with
gdbserver, then we can be sure that the IPA will derive the same
target description as gdbserver, and we would avoid all this index
passing business, which has made this commit so very, very painful.

** END OF AN ASIDE **

Currently then for x86/linux, gdbserver sends a number between 0 and 7
to the IPA, and the IPA uses this to create a target description.

However, I am proposing that gdbserver should now create one of (up
to) 64 different target descriptions for i386, so this 0 to 7 index
isn't going to be good enough any more (amd64 and x32 have slightly
fewer possible target descriptions, but still more than 8, so the
problem is the same).

For a while I wondered if I was going to have to try and find some
backward compatible solution for this mess.  But after seeing how
lightly the IPA is actually documented, I wonder if it is not the case
that there is a tight coupling between a version of gdbserver and a
version of the IPA?  At least I'm hoping so.

In this commit I have thrown out the old IPA target description index
numbering scheme, and switched to a completely new numbering scheme.
Instead of the index that is passed being arbitrary, the index is
instead calculated from the set of cpu features that are present on
the target.  Within the IPA we can then reverse this logic to recreate
the xcr0 value based on the index, and from the xcr0 value we can
create the correct target description.

With the gdbserver to IPA numbering scheme issue resolved I have then
update the gdbserver versions of amd64_linux_read_description and
i386_linux_read_description so that they create target descriptions
using the same set of cpu features as GDB itself.

After this gdbserver should now always come up with the same target
description as GDB does on any x86/Linux target.

This commit does not introduce any new code sharing between GDB and
gdbserver as previous commits in this series does.  Instead this
commit is all about bringing GDB and gdbserver into alignment
functionally so that the next commit can merge the GDB and gdbserver
versions of these functions.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-amd64-ipa.cc |  43 +----
 gdbserver/linux-i386-ipa.cc  |  23 +--
 gdbserver/linux-x86-low.cc   |  15 +-
 gdbserver/linux-x86-tdesc.cc | 315 +++++++++++++++++++++++++----------
 gdbserver/linux-x86-tdesc.h  |  49 +++---
 5 files changed, 277 insertions(+), 168 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index f97b0d6a1d9..42de7d2da16 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -169,47 +169,21 @@ supply_static_tracepoint_registers (struct regcache *regcache,
 
 #endif /* HAVE_UST */
 
-#if !defined __ILP32__
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-#endif
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
 
 #if defined __ILP32__
-  switch (idx)
-    {
-    case X86_TDESC_SSE:
-      return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-    case X86_TDESC_AVX:
-      return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-    case X86_TDESC_AVX_AVX512:
-      return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
-    default:
-      break;
-    }
+  bool is_x32 = true;
 #else
-  return amd64_linux_read_description (idx2mask[idx], false);
+  bool is_x32 = false;
 #endif
 
-  internal_error ("unknown ipa tdesc index: %d", idx);
+  return amd64_linux_read_description (xcr0, is_x32);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a
@@ -277,11 +251,10 @@ void
 initialize_low_tracepoint (void)
 {
 #if defined __ILP32__
-  amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+  for (auto i = 0; i < x86_linux_x32_ipa_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
 #else
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    amd64_linux_read_description (idx2mask[i], false);
+  for (auto i = 0; i < x86_linux_amd64_ipa_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
 #endif
 }
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 459b8055b5c..246bcb9813b 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -246,28 +246,15 @@ initialize_fast_tracepoint_trampoline_buffer (void)
     }
 }
 
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
-  return i386_linux_read_description (idx2mask[idx]);
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+
+  return i386_linux_read_description (xcr0);
 }
 
 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
@@ -289,6 +276,6 @@ void
 initialize_low_tracepoint (void)
 {
   initialize_fast_tracepoint_trampoline_buffer ();
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    i386_linux_read_description (idx2mask[i]);
+  for (auto i = 0; i < x86_linux_i386_ipa_tdesc_count (); i++)
+    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
 }
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 9bf369f8a34..6dbefdb2f26 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -2892,14 +2892,17 @@ x86_target::get_ipa_tdesc_idx ()
   struct regcache *regcache = get_thread_regcache (current_thread, 0);
   const struct target_desc *tdesc = regcache->tdesc;
 
+  if (!use_xml)
+    {
+      if (tdesc == tdesc_i386_linux_no_xml.get ()
 #ifdef __x86_64__
-  return amd64_get_ipa_tdesc_idx (tdesc);
-#endif
-
-  if (tdesc == tdesc_i386_linux_no_xml.get ())
-    return X86_TDESC_SSE;
+	  || tdesc == tdesc_amd64_linux_no_xml.get ()
+#endif /* __x86_64__ */
+	  )
+	return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+    }
 
-  return i386_get_ipa_tdesc_idx (tdesc);
+  return x86_linux_xcr0_to_tdesc_idx (xcr0_storage);
 }
 
 /* The linux target ops object.  */
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 9fd64d8574b..87cf368a336 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -28,96 +28,277 @@
 #include "x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
-/* Return the right x86_linux_tdesc index for a given XCR0.  Return
-   X86_TDESC_LAST if can't find a match.  */
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
 
-static enum x86_linux_tdesc
-xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
 {
-  if (xcr0 & X86_XSTATE_PKRU)
-    {
-      if (is_x32)
-	{
-	  /* No x32 MPX and PKU, fall back to avx_avx512.  */
-	  return X86_TDESC_AVX_AVX512;
-	}
-      else
-	return X86_TDESC_AVX_MPX_AVX512_PKU;
-    }
-  else if (xcr0 & X86_XSTATE_AVX512)
-    return X86_TDESC_AVX_AVX512;
-  else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_ipa_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_ipa_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_ipa_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_AVX_MPX;
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
+	idx |= (1 << i);
     }
-  else if (xcr0 & X86_XSTATE_MPX)
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_MPX;
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
     }
-  else if (xcr0 & X86_XSTATE_AVX)
-    return X86_TDESC_AVX;
-  else if (xcr0 & X86_XSTATE_SSE)
-    return X86_TDESC_SSE;
-  else if (xcr0 & X86_XSTATE_X87)
-    return X86_TDESC_MMX;
-  else
-    return X86_TDESC_LAST;
+
+  return xcr0;
 }
 
+#endif /* IN_PROCESS_AGENT */
+
 #if defined __i386__ || !defined IN_PROCESS_AGENT
 
-static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible i386 target descriptions.  */
 
-/* Return the target description according to XCR0.  */
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 i386_linux_read_description (uint64_t xcr0)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
 
-  struct target_desc **tdesc = &i386_tdescs[idx];
+  target_desc **tdesc = &i386_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = i386_create_target_description (xcr0, true, false);
 
       init_target_desc (*tdesc, i386_expedite_regs);
     }
 
-  return *tdesc;;
+  return *tdesc;
 }
 #endif
 
 #ifdef __x86_64__
 
-static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
-static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
 
-  struct target_desc **tdesc = NULL;
+  target_desc **tdesc = nullptr;
 
   if (is_x32)
     tdesc = &x32_tdescs[idx];
   else
     tdesc = &amd64_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
 
@@ -127,39 +308,3 @@ amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 }
 
 #endif
-
-#ifndef IN_PROCESS_AGENT
-
-int
-i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == i386_tdescs[i])
-	return i;
-    }
-
-  /* If none tdesc is found, return the one with minimum features.  */
-  return X86_TDESC_MMX;
-}
-
-#if defined __x86_64__
-int
-amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == amd64_tdescs[i])
-	return i;
-    }
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == x32_tdescs[i])
-	return i;
-    }
-
-  return X86_TDESC_SSE;
-}
-
-#endif
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index 576aaf5e165..70456e4be44 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -21,29 +21,30 @@
 #ifndef GDBSERVER_LINUX_X86_TDESC_H
 #define GDBSERVER_LINUX_X86_TDESC_H
 
-/* Note: since IPA obviously knows what ABI it's running on (i386 vs x86_64
-   vs x32), it's sufficient to pass only the register set here.  This,
-   together with the ABI known at IPA compile time, maps to a tdesc.  */
-
-enum x86_linux_tdesc {
-  X86_TDESC_MMX = 0,
-  X86_TDESC_SSE = 1,
-  X86_TDESC_AVX = 2,
-  X86_TDESC_MPX = 3,
-  X86_TDESC_AVX_MPX = 4,
-  X86_TDESC_AVX_AVX512 = 5,
-  X86_TDESC_AVX_MPX_AVX512_PKU = 6,
-  X86_TDESC_LAST = 7,
-};
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-int i386_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-#if defined __x86_64__ && !defined IN_PROCESS_AGENT
-int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-const struct target_desc *i386_get_ipa_tdesc (int idx);
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_ipa_tdesc_count ();
+extern int x86_linux_x32_ipa_tdesc_count ();
+extern int x86_linux_i386_ipa_tdesc_count ();
+
+
+#endif /* IN_PROCESS_AGENT */
 
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 8/8] gdb/gdbserver: share x86/linux tdesc caching
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (6 preceding siblings ...)
  2024-03-23 16:35     ` [PATCHv3 7/8] gdbserver: update target description creation for x86/linux Andrew Burgess
@ 2024-03-23 16:35     ` Andrew Burgess
  2024-03-26 12:17       ` Andrew Burgess
  2024-03-25 17:20     ` [PATCHv3 0/8] x86/Linux Target Description Changes Andrew Burgess
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
  9 siblings, 1 reply; 65+ messages in thread
From: Andrew Burgess @ 2024-03-23 16:35 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, hjl.tools, John Baldwin

This commit builds on the previous series of commits to share the
target description caching code between GDB and gdbserver for
x86/Linux targets.

The objective of this commit is to move the four functions (2 each of)
i386_linux_read_description and amd64_linux_read_description into
gdb/nat/x86-linux-tdesc.c and combine them so we have just a single
copy of each.  Then both GDB and gdbserver will link against these
shared functions.

It is worth reading the description of the previous commit to see why
this merging is not as simple as it seems: on the gdbserver side we
actually have two users of these functions, gdbserver itself, and the
in process agent (IPA).

However, the previous commit streamlined the gdbserver code, and so
now it is simple to move the two functions along with all their
support functions from the gdbserver directory into the gdb/nat/
directory, and then GDB is fine to call these functions.

One small curiosity with this patch is the function
x86_linux_post_init_tdesc.  On the gdbserver side the two functions
amd64_linux_read_description and i386_linux_read_description have some
functionality that is not present on the GDB side, that is some
additional configuration that is performed as each target description
is created to setup the expedited registers.

To support this I've added the function x86_linux_post_init_tdesc.
This function is called from the two *_linux_read_description
functions, but is implemented separately for GDB and gdbserver.

This does mean adding back some non-shared code when this whole series
has been about sharing code, but now the only non-shared bit is the
single line that is actually different between GDB and gdbserver, all
the rest, which is identical, is now shared.

I did need to add a new rule to the gdbserver Makefile, this is to
allow the nat/x86-linux-tdesc.c file to be compiled for the IPA.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/amd64-linux-tdep.c       |  31 ----
 gdb/i386-linux-tdep.c        |  30 +---
 gdb/nat/x86-linux-tdesc.c    | 287 +++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  56 ++++++-
 gdbserver/Makefile.in        |   4 +
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 -
 gdbserver/linux-i386-ipa.cc  |   1 -
 gdbserver/linux-x86-low.cc   |   1 -
 gdbserver/linux-x86-tdesc.cc | 285 +---------------------------------
 gdbserver/linux-x86-tdesc.h  |  50 ------
 11 files changed, 355 insertions(+), 393 deletions(-)
 delete mode 100644 gdbserver/linux-x86-tdesc.h

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 7e0900dc6f9..e149034de21 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1579,37 +1579,6 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
-const target_desc *
-amd64_linux_read_description (uint64_t xcr0_features_bit, bool is_x32)
-{
-  static target_desc *amd64_linux_tdescs \
-    [2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  static target_desc *x32_linux_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/] = {};
-
-  target_desc **tdesc;
-
-  if (is_x32)
-    {
-      tdesc = &x32_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0 ]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-  else
-    {
-      tdesc = &amd64_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_MPX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-
-  if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0_features_bit, is_x32,
-					      true, true);
-
-  return *tdesc;
-}
-
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index f5f7a36bf36..ed1442236e8 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -681,29 +681,12 @@ i386_linux_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
 					  layout) != 0;
 }
 
-/* See i386-linux-tdep.h.  */
+/* See nat/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  if (xcr0 == 0)
-    return NULL;
-
-  static struct target_desc *i386_linux_tdescs \
-    [2/*X87*/][2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  struct target_desc **tdesc;
-
-  tdesc = &i386_linux_tdescs[(xcr0 & X86_XSTATE_X87) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
-
-  if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true, false);
-
-  return *tdesc;
+  /* Nothing.  */
 }
 
 /* Get Linux/x86 target description from core dump.  */
@@ -716,7 +699,10 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
   /* Linux/i386.  */
   x86_xsave_layout layout;
   uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
-  const struct target_desc *tdesc = i386_linux_read_description (xcr0);
+
+  const struct target_desc *tdesc;
+  if (xcr0 != 0)
+    tdesc = i386_linux_read_description (xcr0);
 
   if (tdesc != NULL)
     return tdesc;
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index be7014d2b5f..c438dfae84f 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -38,6 +38,8 @@
 #include <sys/user.h>
 #include <sys/user.h>
 
+#ifndef IN_PROCESS_AGENT
+
 /* See nat/x86-linux-tdesc.h.  */
 
 const target_desc *
@@ -122,3 +124,288 @@ x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
 
   gdb_assert_not_reached ("failed to return tdesc");
 }
+
+#endif /* !IN_PROCESS_AGENT */
+
+
+
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
+
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_ipa_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_ipa_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_ipa_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
+	idx |= (1 << i);
+    }
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
+    }
+
+  return xcr0;
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+#if defined __i386__ || !defined IN_PROCESS_AGENT
+
+/* A cache of all possible i386 target descriptions.  */
+
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const struct target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
+
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
+
+  target_desc **tdesc = &i386_tdescs[idx];
+
+  if (*tdesc == nullptr)
+    {
+      *tdesc = i386_create_target_description (xcr0, true, false);
+
+      x86_linux_post_init_tdesc (*tdesc, false);
+    }
+
+  return *tdesc;
+}
+#endif
+
+#ifdef __x86_64__
+
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+{
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
+
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
+
+  target_desc **tdesc = nullptr;
+
+  if (is_x32)
+    tdesc = &x32_tdescs[idx];
+  else
+    tdesc = &amd64_tdescs[idx];
+
+  if (*tdesc == nullptr)
+    {
+      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
+
+      x86_linux_post_init_tdesc (*tdesc, true);
+    }
+  return *tdesc;
+}
+
+#endif
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
index 3727a8bf95e..648fe0ed0b2 100644
--- a/gdb/nat/x86-linux-tdesc.h
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -24,6 +24,8 @@
 
 struct target_desc;
 
+#ifndef IN_PROCESS_AGENT
+
 /* Return the target description for Linux thread TID.
 
    When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
@@ -57,19 +59,57 @@ x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
 			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
 			 const char *error_msg, uint64_t *xcr0_storage);
 
+#endif /* !IN_PROCESS_AGENT */
+
 #ifdef __x86_64__
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  This is implemented separately in both
-   GDB and gdbserver.  */
+/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
 
-extern const target_desc *amd64_linux_read_description
-	(uint64_t xcr0_features_bit, bool is_x32);
+extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
+							bool is_x32);
 
-#endif
+#endif /* __x86_64__ */
+
+/* Return the i386 target description corresponding to XCR0.  */
 
-/* Return the target description according to XCR0.  This is implemented
-   separately in both GDB and gdbserver.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
+/* This function is called from amd64_linux_read_description and
+   i386_linux_read_description after a new target description has been
+   created, TDESC is the new target description, IS_64BIT will be true
+   when called from amd64_linux_read_description, otherwise IS_64BIT will
+   be false.  If the *_linux_read_description functions found a cached
+   target description then this function will not be called.
+
+   Both GDB and gdbserver have their own implementations of this
+   function.  */
+
+extern void x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit);
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_ipa_tdesc_count ();
+extern int x86_linux_x32_ipa_tdesc_count ();
+extern int x86_linux_i386_ipa_tdesc_count ();
+
+#endif /* IN_PROCESS_AGENT */
+
 #endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index c7120895a26..bd6f68e7f2c 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -519,6 +519,10 @@ gdbsupport/%-ipa.o: ../gdbsupport/%.cc
 	$(IPAGENT_COMPILE) $<
 	$(POSTCOMPILE)
 
+nat/%-ipa.o: ../gdb/nat/%.c
+	$(IPAGENT_COMPILE) $<
+	$(POSTCOMPILE)
+
 %-ipa.o: ../gdb/%.c
 	$(IPAGENT_COMPILE) -x c++ $<
 	$(POSTCOMPILE)
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7a2702d78bf..36a457f21ed 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -116,6 +116,7 @@ case "${gdbserver_host}" in
 			srv_linux_btrace=yes
 			ipa_obj="linux-i386-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/i386-ipa.o"
+			ipa_obj="${ipa_obj} nat/x86-linux-tdesc-ipa.o"
 			;;
   i[34567]86-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
@@ -380,6 +381,7 @@ case "${gdbserver_host}" in
 			srv_linux_btrace=yes
 			ipa_obj="linux-amd64-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/amd64-ipa.o"
+			ipa_obj="${ipa_obj} nat/x86-linux-tdesc-ipa.o"
 			;;
   x86_64-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o"
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 42de7d2da16..afa6aab5bab 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -21,7 +21,6 @@
 #include "server.h"
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux-tdesc.h"
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 246bcb9813b..f6d26f784af 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -21,7 +21,6 @@
 #include "server.h"
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "nat/x86-linux-tdesc.h"
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 6dbefdb2f26..7134d20dd7f 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -47,7 +47,6 @@
 #include "nat/linux-nat.h"
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 87cf368a336..39d1d51b8c0 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -19,292 +19,19 @@
 
 #include "server.h"
 #include "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#endif
 #include "x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
-/* A structure used to describe a single cpu feature that might, or might
-   not, be checked for when creating a target description for one of i386,
-   amd64, or x32.  */
-
-struct x86_tdesc_feature {
-  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
-  uint64_t feature;
-
-  /* Is this feature checked when creating an i386 target description.  */
-  bool is_i386;
-
-  /* Is this feature checked when creating an amd64 target description.  */
-  bool is_amd64;
-
-  /* Is this feature checked when creating an x32 target description.  */
-  bool is_x32;
-};
-
-/* A constant table that describes all of the cpu features that are
-   checked when building a target description for i386, amd64, or x32.  */
-
-static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
-  /* Feature,           i386,	amd64,	x32.  */
-  { X86_XSTATE_PKRU,	true,	true, 	true },
-  { X86_XSTATE_AVX512,	true,	true, 	true },
-  { X86_XSTATE_AVX,	true,	true, 	true },
-  { X86_XSTATE_MPX,	true,	true, 	false },
-  { X86_XSTATE_SSE,	true,	false, 	false },
-  { X86_XSTATE_X87,	true,	false, 	false }
-};
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an i386 target description.  */
-
-static constexpr uint64_t
-x86_linux_i386_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an amd64 target description.  */
-
-static constexpr uint64_t
-x86_linux_amd64_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an x32 target description.  */
-
-static constexpr uint64_t
-x86_linux_x32_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an i386 target description.  */
-
-static constexpr int
-x86_linux_i386_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an amd64 target description.  */
-
-static constexpr int
-x86_linux_amd64_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an x32 target description.  */
-
-static constexpr int
-x86_linux_x32_tdesc_count ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_amd64_ipa_tdesc_count ()
-{
-  return x86_linux_amd64_tdesc_count ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_x32_ipa_tdesc_count ()
-{
-  return x86_linux_x32_tdesc_count ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_i386_ipa_tdesc_count ()
-{
-  return x86_linux_i386_tdesc_count ();
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
-{
-  /* The following table shows which features are checked for when creating
-     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
-     represents the bit order within the generated index number.
-
-     i386  | x87 sse mpx avx avx512 pkru
-     amd64 |         mpx avx avx512 pkru
-     i32   |             avx avx512 pkru
-
-     The features are ordered so that for each mode (i386, amd64, i32) the
-     generated index will form a continuous range.  */
-
-  int idx = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((xcr0 & x86_linux_all_tdesc_features[i].feature) != 0)
-	idx |= (1 << i);
-    }
-
-  return idx;
-}
-
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
-{
-  uint64_t xcr0 = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_tdesc_features[i].feature;
-    }
-
-  return xcr0;
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-/* A cache of all possible i386 target descriptions.  */
-
-static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count ()] = { };
-
 /* See nat/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count ());
-
-  target_desc **tdesc = &i386_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = i386_create_target_description (xcr0, true, false);
-
-      init_target_desc (*tdesc, i386_expedite_regs);
-    }
-
-  return *tdesc;
-}
-#endif
-
 #ifdef __x86_64__
-
-/* A cache of all possible amd64 target descriptions.  */
-
-static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count ()] = { };
-
-/* A cache of all possible x32 target descriptions.  */
-
-static target_desc *x32_tdescs[x86_linux_x32_tdesc_count ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
-  if (is_x32)
-    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  if (is_64bit)
+    init_target_desc (tdesc, amd64_expedite_regs);
   else
-    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
-
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  if (is_x32)
-    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count ());
-  else
-    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count ());
-
-  target_desc **tdesc = nullptr;
-
-  if (is_x32)
-    tdesc = &x32_tdescs[idx];
-  else
-    tdesc = &amd64_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
-      init_target_desc (*tdesc, amd64_expedite_regs);
-    }
-  return *tdesc;
+#endif
+    init_target_desc (tdesc, i386_expedite_regs);
 }
 
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
deleted file mode 100644
index 70456e4be44..00000000000
--- a/gdbserver/linux-x86-tdesc.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Low level support for x86 (i386 and x86-64), shared between gdbserver
-   and IPA.
-
-   Copyright (C) 2016-2024 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   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 <http://www.gnu.org/licenses/>.  */
-
-#ifndef GDBSERVER_LINUX_X86_TDESC_H
-#define GDBSERVER_LINUX_X86_TDESC_H
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
-
-/* Within the in-process-agent we need to pre-initialise all of the target
-   descriptions, to do this we need to know how many target descriptions
-   there are for each different target type.  These functions return the
-   target description count for the relevant target.  */
-
-extern int x86_linux_amd64_ipa_tdesc_count ();
-extern int x86_linux_x32_ipa_tdesc_count ();
-extern int x86_linux_i386_ipa_tdesc_count ();
-
-
-#endif /* IN_PROCESS_AGENT */
-
-#endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8]  x86/Linux Target Description Changes
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (7 preceding siblings ...)
  2024-03-23 16:35     ` [PATCHv3 8/8] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
@ 2024-03-25 17:20     ` Andrew Burgess
  2024-03-25 18:26       ` Simon Marchi
  2024-03-26 16:36       ` Andrew Burgess
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
  9 siblings, 2 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-25 17:20 UTC (permalink / raw)
  To: gdb-patches; +Cc: hjl.tools

Andrew Burgess <aburgess@redhat.com> writes:

> In v3:
>
>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>     I've resolved, but am unable to test.  Reposting so the author of
>     that other commit can validate.
>
>   - Initial testing looks good.  Full tests are still running.

Testing completed with no issues.  H.J. Lu confirmed that this versions
didn't break the x32 behaviour.  I've gone ahead and pushed these
patches.

If anything crops up then do let me know.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8] x86/Linux Target Description Changes
  2024-03-25 17:20     ` [PATCHv3 0/8] x86/Linux Target Description Changes Andrew Burgess
@ 2024-03-25 18:26       ` Simon Marchi
  2024-03-26 12:15         ` Andrew Burgess
  2024-03-26 16:36       ` Andrew Burgess
  1 sibling, 1 reply; 65+ messages in thread
From: Simon Marchi @ 2024-03-25 18:26 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: hjl.tools

On 3/25/24 13:20, Andrew Burgess wrote:
> Andrew Burgess <aburgess@redhat.com> writes:
> 
>> In v3:
>>
>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>>     I've resolved, but am unable to test.  Reposting so the author of
>>     that other commit can validate.
>>
>>   - Initial testing looks good.  Full tests are still running.
> 
> Testing completed with no issues.  H.J. Lu confirmed that this versions
> didn't break the x32 behaviour.  I've gone ahead and pushed these
> patches.
> 
> If anything crops up then do let me know.
> 
> Thanks,
> Andrew
> 

Hi Andrew,

I think your series introduces some build failures with Clang.  One is
easy, it's a missing `-x c++` in gdbserver/Makefile.in.

The other is:

      CXX    nat/x86-linux-tdesc-ipa.o
    /home/smarchi/src/binutils-gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:167:1: error: unused function 'x86_linux_i386_tdesc_feature_mask' [-Werror,-Wunused-function]
      167 | x86_linux_i386_tdesc_feature_mask ()
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It's possible that this function just needs to be moved in the same
"#ifdef" as where it's used, but since I didn't follow your work
closely, I prefer to let you fix it, in case I'm missing something.

Thanks,

Simon

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/7] gdbserver: update target description creation for x86/linux
  2024-03-21 17:28         ` John Baldwin
@ 2024-03-26 10:01           ` Luis Machado
  2024-03-26 15:31             ` Tom Tromey
  0 siblings, 1 reply; 65+ messages in thread
From: Luis Machado @ 2024-03-26 10:01 UTC (permalink / raw)
  To: John Baldwin, Andrew Burgess, gdb-patches

On 3/21/24 17:28, John Baldwin wrote:
> On 3/19/24 11:34 AM, Andrew Burgess wrote:
>> John Baldwin <jhb@FreeBSD.org> writes:
>>
>>> On 3/5/24 9:00 AM, Andrew Burgess wrote:
>>>> This commit is part of a series which aims to share more of the target
>>>> description creation between GDB and gdbserver for x86/Linux.
>>>>
>>>> After some refactoring, the previous commit actually started to share
>>>> some code, we added the shared x86_linux_tdesc_for_tid function into
>>>> nat/x86-linux-tdesc.c.  However, this function still relies on
>>>> amd64_linux_read_description and i386_linux_read_description which are
>>>> implemented separately for both gdbserver and GDB.  Given that at
>>>> their core, all these functions to is:
>>>>
>>>>     1. take an xcr0 value as input,
>>>>     2. mask out some feature bits,
>>>>     3. look for a cached pre-generated target description and return it
>>>>        if found,
>>>>     4. if no cached target description is found then call either
>>>>        amd64_create_target_description or
>>>>        i386_create_target_description to create a new target
>>>>        description, which is then added to the cache.  Return the newly
>>>>        created target description.
>>>>
>>>> The inner functions amd64_create_target_description and
>>>> i386_create_target_description are already shared between GDB and
>>>> gdbserver (in the gdb/arch/ directory), so the only thing that
>>>> the *_read_description functions really do is add the caching layer,
>>>> and it feels like this really could be shared.
>>>>
>>>> However, we have a small problem.
>>>>
>>>> On the GDB side we create target descriptions using a different set of
>>>> cpu features than on the gdbserver side!  This means that for the
>>>> exact same target, we might get a different target description when
>>>> using native GDB vs using gdbserver.  This surely feels like a
>>>> mistake, I would expect to get the same target description on each.
>>>>
>>>> The table below shows the number of possible different target
>>>> descriptions that we can create on the GDB side vs on the gdbserver
>>>> side for each target type:
>>>>
>>>>           | GDB | gdbserver
>>>>     ------|-----|----------
>>>>     i386  | 64  | 7
>>>>     amd64 | 32  | 7
>>>>     x32   | 16  | 7
>>>>
>>>> So in theory, all I want to do is move the GDB version
>>>> of *_read_description into the nat/ directory and have gdbserver use
>>>> that, then both GDB and gdbserver would be able to create any of the
>>>> possible target descriptions.
>>>>
>>>> Unfortunately it's a little more complex than that due to the in
>>>> process agent (IPA).
>>>>
>>>> When the IPA is in use, gdbserver sends a target description index to
>>>> the IPA, and the IPA uses this to find the correct target description
>>>> to use.
>>>>
>>>> ** START OF AN ASIDE **
>>>>
>>>> Back in the day I suspect this approach made perfect sense.  However
>>>> since this commit:
>>>>
>>>>     commit a8806230241d201f808d856eaae4d44088117b0c
>>>>     Date:   Thu Dec 7 17:07:01 2017 +0000
>>>>
>>>>         Initialize target description early in IPA
>>>>
>>>> I think passing the index is now more trouble than its worth.
>>>>
>>>> We used to pass the index, and then use that index to lookup which
>>>> target description to instantiate and use.  However, the above commit
>>>> fixed an issue where we can't call malloc() within (certain parts of)
>>>> the IPA (apparently), so instead we now pre-compute _every_ possible
>>>> target description within the IPA.  The index is now only used to
>>>> lookup which of the (many) pre-computed target descriptions to use.
>>>>
>>>> It would (I think) have been easier all around if the IPA just
>>>> self-inspected, figured out its own xcr0 value, and used that to
>>>> create the one target description that is required.  So long as the
>>>> xcr0 to target description code is shared (at compile time) with
>>>> gdbserver, then we can be sure that the IPA will derive the same
>>>> target description as gdbserver, and we would avoid all this index
>>>> passing business, which has made this commit so very, very painful.
>>>>
>>>> ** END OF AN ASIDE **
>>>>
>>>> Currently then for x86/linux, gdbserver sends a number between 0 and 7
>>>> to the IPA, and the IPA uses this to create a target description.
>>>>
>>>> However, I am proposing that gdbserver should now create one of (up
>>>> to) 64 different target descriptions for i386, so this 0 to 7 index
>>>> isn't going to be good enough any more (amd64 and x32 have slightly
>>>> fewer possible target descriptions, but still more than 8, so the
>>>> problem is the same).
>>>>
>>>> For a while I wondered if I was going to have to try and find some
>>>> backward compatible solution for this mess.  But after seeing how
>>>> lightly the IPA is actually documented, I wonder if it is not the case
>>>> that there is a tight coupling between a version of gdbserver and a
>>>> version of the IPA?  At least I'm hoping so.
>>>>
>>>> In this commit I have thrown out the old IPA target description index
>>>> numbering scheme, and switched to a completely new numbering scheme.
>>>> Instead of the index that is passed being arbitrary, the index is
>>>> instead calculated from the set of cpu features that are present on
>>>> the target.  Within the IPA we can then reverse this logic to recreate
>>>> the xcr0 value based on the index, and from the xcr0 value we can
>>>> create the correct target description.
>>>>
>>>> With the gdbserver to IPA numbering scheme issue resolved I have then
>>>> update the gdbserver versions of amd64_linux_read_description and
>>>> i386_linux_read_description so that they create target descriptions
>>>> using the same set of cpu features as GDB itself.
>>>>
>>>> After this gdbserver should now always come up with the same target
>>>> description as GDB does on any x86/Linux target.
>>>>
>>>> This commit does not introduce any new code sharing between GDB and
>>>> gdbserver as previous commits in this series does.  Instead this
>>>> commit is all about bringing GDB and gdbserver into alignment
>>>> functionally so that the next commit can merge the GDB and gdbserver
>>>> versions of these functions.
>>>
>>> It does seem like the IPA case should just compute the tdesc it needs
>>> if I understand you correctly.
>>
>> Yes, totally.  And it's not like it would even add much code to the IPA,
>> I believe it already pulls in all of the target description creation
>> code, the only thing that is "saved" is the code that probes the
>> hardware to figure out which description we should use.
>>
>> This whole area seems like it has not aged well :-/
>>
>>>
>>> BTW, on other arches like aarch64 and I believe risc-v, a flat array
>>> is not used for the tdesc cache.  Now the cache is a std::unordered_map<>
>>> and there is a 'struct foo_features' that has an operator== and
>>> std::hash<> specialization.  It really would be nice if x86 was the same,
>>> but that would require abandoning a notion of a portable "index" shared
>>> between gdbserver and the IPA.
>>
>> Indeed.  I did the worked on the feature/hash/lookup for RISC-V so I had
>> this in mind when looking at this code.  Originally I had planned to
>> switch to a map style cache, but when I found the IPA/index passing code
>> I lost my nerve and tried to find a solution that was inline with what
>> we had currently.
>>
>> I don't really know about the IPA enough to make claims about what
>> is/isn't a reasonable change in this area.
> 
> I have zero knowledge about the IPA myself.  OTOH, if no one else steps
> up with a strong opinion here, I would suggest that you go ahead and
> fix the IPA as you described so it is more self-contained and then we can
> use a saner approach for dealing with tdesc caching for x86.
> 

Should we start thinking of deprecating the IPA if no one shows any interest in
keeping it alive? It is there to provide support for (fast/static) tracepoints,
and I haven't seen anyone using it in the open for years now.

The cost of maintenance may not justify keeping it alive.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8] x86/Linux Target Description Changes
  2024-03-25 18:26       ` Simon Marchi
@ 2024-03-26 12:15         ` Andrew Burgess
  2024-03-26 13:51           ` H.J. Lu
  0 siblings, 1 reply; 65+ messages in thread
From: Andrew Burgess @ 2024-03-26 12:15 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: hjl.tools

Simon Marchi <simark@simark.ca> writes:

> On 3/25/24 13:20, Andrew Burgess wrote:
>> Andrew Burgess <aburgess@redhat.com> writes:
>> 
>>> In v3:
>>>
>>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>>>     I've resolved, but am unable to test.  Reposting so the author of
>>>     that other commit can validate.
>>>
>>>   - Initial testing looks good.  Full tests are still running.
>> 
>> Testing completed with no issues.  H.J. Lu confirmed that this versions
>> didn't break the x32 behaviour.  I've gone ahead and pushed these
>> patches.
>> 
>> If anything crops up then do let me know.
>> 
>> Thanks,
>> Andrew
>> 
>
> Hi Andrew,
>
> I think your series introduces some build failures with Clang.  One is
> easy, it's a missing `-x c++` in gdbserver/Makefile.in.

Thanks for fixing this one.

>
> The other is:
>
>       CXX    nat/x86-linux-tdesc-ipa.o
>     /home/smarchi/src/binutils-gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:167:1: error: unused function 'x86_linux_i386_tdesc_feature_mask' [-Werror,-Wunused-function]
>       167 | x86_linux_i386_tdesc_feature_mask ()
>           | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> It's possible that this function just needs to be moved in the same
> "#ifdef" as where it's used, but since I didn't follow your work
> closely, I prefer to let you fix it, in case I'm missing something.

Sorry for the breakage.

I pushed the patch below to resolve this issue.

Thanks,
Andrew

--

commit f4c19f89ef43dbce8065532c808e1aeb05d08994
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Tue Mar 26 12:09:27 2024 +0000

    gdb/gdbserver: fix some defined but unused function warnings
    
    This commit:
    
      commit 198ff6ff819c240545f9fc68b39636fd376d4ba9
      Date:   Tue Jan 30 15:37:23 2024 +0000
    
          gdb/gdbserver: share x86/linux tdesc caching
    
    added some functions which are always defined, but their use is
    guarded within various #ifdef blocks.  As a result we were seeing
    errors about defined, but unused, functions.
    
    I've fixed this problem in this commit by wrapping the function
    definitions within #ifdef blocks.
    
    I'm a little worried that there might be too many #ifdef blocks within
    this file, however, I'm going to commit this fix for now as this will
    fix the build, then I'll think about if there's a better way to split
    this file so we might avoid some of these #ifdef blocks.

diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index c438dfae84f..8a02f77fa6a 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -160,6 +160,8 @@ static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
   { X86_XSTATE_X87,	true,	false, 	false }
 };
 
+#if defined __i386__ || !defined IN_PROCESS_AGENT
+
 /* Return a compile time constant which is a mask of all the cpu features
    that are checked for when building an i386 target description.  */
 
@@ -175,6 +177,10 @@ x86_linux_i386_tdesc_feature_mask ()
   return mask;
 }
 
+#endif /* __i386__ || !IN_PROCESS_AGENT */
+
+#ifdef __x86_64__
+
 /* Return a compile time constant which is a mask of all the cpu features
    that are checked for when building an amd64 target description.  */
 
@@ -205,6 +211,8 @@ x86_linux_x32_tdesc_feature_mask ()
   return mask;
 }
 
+#endif /* __x86_64__ */
+
 /* Return a compile time constant which is a count of the number of cpu
    features that are checked for when building an i386 target description.  */
 
@@ -222,6 +230,8 @@ x86_linux_i386_tdesc_count ()
   return (1 << count);
 }
 
+#if defined __x86_64__ || defined IN_PROCESS_AGENT
+
 /* Return a compile time constant which is a count of the number of cpu
    features that are checked for when building an amd64 target description.  */
 
@@ -256,6 +266,8 @@ x86_linux_x32_tdesc_count ()
   return (1 << count);
 }
 
+#endif /* __x86_64__ || IN_PROCESS_AGENT */
+
 #ifdef IN_PROCESS_AGENT
 
 /* See linux-x86-tdesc.h.  */


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 8/8] gdb/gdbserver: share x86/linux tdesc caching
  2024-03-23 16:35     ` [PATCHv3 8/8] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
@ 2024-03-26 12:17       ` Andrew Burgess
  0 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-26 12:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: hjl.tools, John Baldwin


Simon pointed out some build issues encountered when building with
Clang.  While test building using Clang an additional issue was
highlighted, which I've fixed by pushing the patch below.

Sorry for anyone who ran into this issue.

Thanks,
Andrew

---

commit 24df37a10f8773ad5db07dc000f694d6405e3a36
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Tue Mar 26 12:05:07 2024 +0000

    gdb: fix possible uninitialised variable use
    
    After this commit:
    
      commit 198ff6ff819c240545f9fc68b39636fd376d4ba9
      Date:   Tue Jan 30 15:37:23 2024 +0000
    
          gdb/gdbserver: share x86/linux tdesc caching
    
    a possible use of an uninitialised variable was introduced, the
    'tdesc' variable in i386_linux_core_read_description might be read
    without being written too if 'xcr0' was 0.
    
    This is fixed in this commit.  I've updated the function to follow the
    same pattern as amd64_linux_core_read_description, if xcr0 is 0 then
    we select a default xcr0 value and use that to select a tdesc.

diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index ed1442236e8..2e84384306d 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -700,17 +700,15 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
   x86_xsave_layout layout;
   uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
 
-  const struct target_desc *tdesc;
-  if (xcr0 != 0)
-    tdesc = i386_linux_read_description (xcr0);
-
-  if (tdesc != NULL)
-    return tdesc;
+  if (xcr0 == 0)
+    {
+      if (bfd_get_section_by_name (abfd, ".reg-xfp") != nullptr)
+	xcr0 = X86_XSTATE_SSE_MASK;
+      else
+	xcr0 = X86_XSTATE_X87_MASK;
+    }
 
-  if (bfd_get_section_by_name (abfd, ".reg-xfp") != NULL)
-    return i386_linux_read_description (X86_XSTATE_SSE_MASK);
-  else
-    return i386_linux_read_description (X86_XSTATE_X87_MASK);
+  return i386_linux_read_description (xcr0);
 }
 
 /* Similar to i386_supply_fpregset, but use XSAVE extended state.  */


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8] x86/Linux Target Description Changes
  2024-03-26 12:15         ` Andrew Burgess
@ 2024-03-26 13:51           ` H.J. Lu
  2024-03-26 14:16             ` H.J. Lu
  0 siblings, 1 reply; 65+ messages in thread
From: H.J. Lu @ 2024-03-26 13:51 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Simon Marchi, gdb-patches

On Tue, Mar 26, 2024 at 5:16 AM Andrew Burgess <aburgess@redhat.com> wrote:
>
> Simon Marchi <simark@simark.ca> writes:
>
> > On 3/25/24 13:20, Andrew Burgess wrote:
> >> Andrew Burgess <aburgess@redhat.com> writes:
> >>
> >>> In v3:
> >>>
> >>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
> >>>     I've resolved, but am unable to test.  Reposting so the author of
> >>>     that other commit can validate.
> >>>
> >>>   - Initial testing looks good.  Full tests are still running.
> >>
> >> Testing completed with no issues.  H.J. Lu confirmed that this versions
> >> didn't break the x32 behaviour.  I've gone ahead and pushed these
> >> patches.
> >>
> >> If anything crops up then do let me know.
> >>
> >> Thanks,
> >> Andrew
> >>
> >
> > Hi Andrew,
> >
> > I think your series introduces some build failures with Clang.  One is
> > easy, it's a missing `-x c++` in gdbserver/Makefile.in.
>
> Thanks for fixing this one.
>
> >
> > The other is:
> >
> >       CXX    nat/x86-linux-tdesc-ipa.o
> >     /home/smarchi/src/binutils-gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:167:1: error: unused function 'x86_linux_i386_tdesc_feature_mask' [-Werror,-Wunused-function]
> >       167 | x86_linux_i386_tdesc_feature_mask ()
> >           | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > It's possible that this function just needs to be moved in the same
> > "#ifdef" as where it's used, but since I didn't follow your work
> > closely, I prefer to let you fix it, in case I'm missing something.
>
> Sorry for the breakage.
>
> I pushed the patch below to resolve this issue.
>

gdbserver from your old patch:

https://patchwork.sourceware.org/project/gdb/list/?series=32189

works on x32.  But the current master gives me:

/export/gnu/import/git/sources/gdb/gdbserver/regcache.cc:273: A
problem internal to GDBserver has been detected.
Unknown register bnd0raw requested

again.

-- 
H.J.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8] x86/Linux Target Description Changes
  2024-03-26 13:51           ` H.J. Lu
@ 2024-03-26 14:16             ` H.J. Lu
  0 siblings, 0 replies; 65+ messages in thread
From: H.J. Lu @ 2024-03-26 14:16 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Simon Marchi, gdb-patches

On Tue, Mar 26, 2024 at 6:51 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Mar 26, 2024 at 5:16 AM Andrew Burgess <aburgess@redhat.com> wrote:
> >
> > Simon Marchi <simark@simark.ca> writes:
> >
> > > On 3/25/24 13:20, Andrew Burgess wrote:
> > >> Andrew Burgess <aburgess@redhat.com> writes:
> > >>
> > >>> In v3:
> > >>>
> > >>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
> > >>>     I've resolved, but am unable to test.  Reposting so the author of
> > >>>     that other commit can validate.
> > >>>
> > >>>   - Initial testing looks good.  Full tests are still running.
> > >>
> > >> Testing completed with no issues.  H.J. Lu confirmed that this versions
> > >> didn't break the x32 behaviour.  I've gone ahead and pushed these
> > >> patches.
> > >>
> > >> If anything crops up then do let me know.
> > >>
> > >> Thanks,
> > >> Andrew
> > >>
> > >
> > > Hi Andrew,
> > >
> > > I think your series introduces some build failures with Clang.  One is
> > > easy, it's a missing `-x c++` in gdbserver/Makefile.in.
> >
> > Thanks for fixing this one.
> >
> > >
> > > The other is:
> > >
> > >       CXX    nat/x86-linux-tdesc-ipa.o
> > >     /home/smarchi/src/binutils-gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:167:1: error: unused function 'x86_linux_i386_tdesc_feature_mask' [-Werror,-Wunused-function]
> > >       167 | x86_linux_i386_tdesc_feature_mask ()
> > >           | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > >
> > > It's possible that this function just needs to be moved in the same
> > > "#ifdef" as where it's used, but since I didn't follow your work
> > > closely, I prefer to let you fix it, in case I'm missing something.
> >
> > Sorry for the breakage.
> >
> > I pushed the patch below to resolve this issue.
> >
>
> gdbserver from your old patch:
>
> https://patchwork.sourceware.org/project/gdb/list/?series=32189
>
> works on x32.  But the current master gives me:
>
> /export/gnu/import/git/sources/gdb/gdbserver/regcache.cc:273: A
> problem internal to GDBserver has been detected.
> Unknown register bnd0raw requested
>
> again.

Never mind.  I was using the wrong source.  x32 works.

-- 
H.J.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/7] gdbserver: update target description creation for x86/linux
  2024-03-26 10:01           ` Luis Machado
@ 2024-03-26 15:31             ` Tom Tromey
  0 siblings, 0 replies; 65+ messages in thread
From: Tom Tromey @ 2024-03-26 15:31 UTC (permalink / raw)
  To: Luis Machado; +Cc: John Baldwin, Andrew Burgess, gdb-patches

>>>>> "Luis" == Luis Machado <luis.machado@arm.com> writes:

Luis> Should we start thinking of deprecating the IPA if no one shows any interest in
Luis> keeping it alive? It is there to provide support for (fast/static) tracepoints,
Luis> and I haven't seen anyone using it in the open for years now.

Luis> The cost of maintenance may not justify keeping it alive.

This makes sense to me as well.

thanks,
Tom

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8]  x86/Linux Target Description Changes
  2024-03-25 17:20     ` [PATCHv3 0/8] x86/Linux Target Description Changes Andrew Burgess
  2024-03-25 18:26       ` Simon Marchi
@ 2024-03-26 16:36       ` Andrew Burgess
  2024-03-26 19:03         ` Andrew Burgess
  1 sibling, 1 reply; 65+ messages in thread
From: Andrew Burgess @ 2024-03-26 16:36 UTC (permalink / raw)
  To: gdb-patches; +Cc: hjl.tools

Andrew Burgess <aburgess@redhat.com> writes:

> Andrew Burgess <aburgess@redhat.com> writes:
>
>> In v3:
>>
>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>>     I've resolved, but am unable to test.  Reposting so the author of
>>     that other commit can validate.
>>
>>   - Initial testing looks good.  Full tests are still running.
>
> Testing completed with no issues.  H.J. Lu confirmed that this versions
> didn't break the x32 behaviour.  I've gone ahead and pushed these
> patches.
>
> If anything crops up then do let me know.

I'm aware that this series broke pretty much anything that was not x86
based when configured with --enable-targets=all.

The problem is that, as part of this commit, I moved generic tdep (arch
specific) code into the nat/ directory as part of an effort to share the
code between GDB and gdbserver.  This was incorrect.

I'm open to reverting this series, however, I'm currently working on a
fix, splitting the nat/ code between nat/ and arch/.  I'm hoping to have
a fix today.  If that doesn't look possible then I'll likely revert this
series before the end of my day.

Apologies for the (serious) breakage.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/8]  x86/Linux Target Description Changes
  2024-03-26 16:36       ` Andrew Burgess
@ 2024-03-26 19:03         ` Andrew Burgess
  0 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-03-26 19:03 UTC (permalink / raw)
  To: gdb-patches; +Cc: hjl.tools

Andrew Burgess <aburgess@redhat.com> writes:

> Andrew Burgess <aburgess@redhat.com> writes:
>
>> Andrew Burgess <aburgess@redhat.com> writes:
>>
>>> In v3:
>>>
>>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>>>     I've resolved, but am unable to test.  Reposting so the author of
>>>     that other commit can validate.
>>>
>>>   - Initial testing looks good.  Full tests are still running.
>>
>> Testing completed with no issues.  H.J. Lu confirmed that this versions
>> didn't break the x32 behaviour.  I've gone ahead and pushed these
>> patches.
>>
>> If anything crops up then do let me know.
>
> I'm aware that this series broke pretty much anything that was not x86
> based when configured with --enable-targets=all.
>
> The problem is that, as part of this commit, I moved generic tdep (arch
> specific) code into the nat/ directory as part of an effort to share the
> code between GDB and gdbserver.  This was incorrect.
>
> I'm open to reverting this series, however, I'm currently working on a
> fix, splitting the nat/ code between nat/ and arch/.  I'm hoping to have
> a fix today.  If that doesn't look possible then I'll likely revert this
> series before the end of my day.
>
> Apologies for the (serious) breakage.

I have now reverted all of the commits from this series, as well as the
small fixes which I pushed earlier today.  I've also reverts the fix
which Simon pushed that was for code from this series.

Splitting the code between nat/ and arch/ mostly worked, but I was still
running into some edge cases which I couldn't get working, so I could
either:

  + push a partial fix, which still left some things broken, or

  + revert everything, take a step back and re-work the series.

I think reverting and coming at this fresh tomorrow is the better
choice.

I apologise for all the churn.  The post-revert branch seemed to build
fine, so hopefully I've not left anything in a broken state.

Let me know if I've left anything broken, I'll check back in a couple of
hours to see if there is more fixing needed.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 00/10] x86/Linux Target Description Changes
  2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
                       ` (8 preceding siblings ...)
  2024-03-25 17:20     ` [PATCHv3 0/8] x86/Linux Target Description Changes Andrew Burgess
@ 2024-04-05 12:33     ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 01/10] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
                         ` (12 more replies)
  9 siblings, 13 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

In v4:

  - I tried merging V3, but it turned out I broke pretty much
    everything that wasn't x86 based when configured with
    --enable-targets=all,

  - The problem was a failure to correctly split the shared code
    between the gdb/arch/ and gdb/nat/ directories, as a consequence,
    code which is needed on a non x86 based host to support x86 based
    targets wasn't available to the compilation, and the build failed,

  - In V4 I've gone through every patch and resplit the code in a way
    which I now believe is correct, I've done the following tests:

    + On a non x86 host I've built GDB to support only the current
    host as a target, to support all targets, and to support x86-64
    and i386 linux targets,

    + On an i386 virtual machine I built GDB only for the host as a
    target, and for all targets.  I regression tested the all targets
    build for unix, native-gdbserver, and native-extended-gdbserver,

    + On an x86-64 machine I've built GDB for only the current host as
    a target, and for all targets.  I regression tested the all targets
    build for unix, native-gdbserver, and native-extended-gdbserver.

  - Only patches 6, 8, and 10 require significant review.  All of the
    other patches are pretty trivial (though reviews always welcome).

  - I think there's more improvements that can be made to the x86
    target description creation/lookup/caching.  This series only
    changes the Linux lookup, and we still cache i386/amd64/x32
    separately.

    In the future I think we can merge all x86 target description
    caching into a single data structure, this would be for all OS
    variants and all ABI variants.

    Though making that "grand unification" will certainly require some
    of the code in this series to change, I think the bulk of it will
    remain, and trying to do everything in one series is just going to
    result in an even larger series.  I'd prefer to get these first
    patches merged, then come back to build on this work once this is
    merged and we know there's no problems with it.

In v3:

  - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
    I've resolved, but am unable to test.  Reposting so the author of
    that other commit can validate.

  - Initial testing looks good.  Full tests are still running.

In v2:

  - Rebase to current upstream/master, no merge conflicts,

  - Retested.

---

Andrew Burgess (10):
  gdbserver/ipa/x86: remove unneeded declarations
  gdbserver: convert have_ptrace_getregset to a tribool
  gdb/x86: move reading of cs and ds state into gdb/nat directory
  gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  gdb/gdbserver: share some code relating to target description creation
  gdb/arch: assert that X86_XSTATE_MPX is not set for x32
  gdbserver: update target description creation for x86/linux
  gdb: move xcr0 == 0 check into i386_linux_core_read_description
  gdb/gdbserver: share x86/linux tdesc caching

 gdb/Makefile.in                     |   8 +
 gdb/amd64-linux-tdep.c              |  32 +---
 gdb/amd64-linux-tdep.h              |   6 -
 gdb/arch/amd64-linux-tdesc.c        |  61 +++++++
 gdb/arch/amd64-linux-tdesc.h        |  30 ++++
 gdb/arch/amd64.c                    |   8 +-
 gdb/arch/i386-linux-tdesc.c         |  51 ++++++
 gdb/arch/i386-linux-tdesc.h         |  29 ++++
 gdb/arch/x86-linux-tdesc-features.c | 247 ++++++++++++++++++++++++++++
 gdb/arch/x86-linux-tdesc-features.h |  62 +++++++
 gdb/arch/x86-linux-tdesc.h          |  37 +++++
 gdb/configure.nat                   |   8 +-
 gdb/configure.tgt                   |  11 +-
 gdb/i386-linux-tdep.c               |  42 ++---
 gdb/i386-linux-tdep.h               |  23 ---
 gdb/nat/x86-linux-tdesc.c           | 120 ++++++++++++++
 gdb/nat/x86-linux-tdesc.h           |  60 +++++++
 gdb/nat/x86-linux.c                 |  47 ++++++
 gdb/nat/x86-linux.h                 |  28 ++++
 gdb/x86-linux-nat.c                 | 123 ++------------
 gdbserver/configure.srv             |  11 ++
 gdbserver/linux-amd64-ipa.cc        |  50 ++----
 gdbserver/linux-arm-low.cc          |   6 +-
 gdbserver/linux-i386-ipa.cc         |  26 +--
 gdbserver/linux-low.cc              |   2 +-
 gdbserver/linux-low.h               |   2 +-
 gdbserver/linux-x86-low.cc          | 192 +++++++--------------
 gdbserver/linux-x86-tdesc.cc        | 142 +---------------
 gdbserver/linux-x86-tdesc.h         |  56 -------
 gdbsupport/x86-xstate.h             |  20 +++
 30 files changed, 949 insertions(+), 591 deletions(-)
 create mode 100644 gdb/arch/amd64-linux-tdesc.c
 create mode 100644 gdb/arch/amd64-linux-tdesc.h
 create mode 100644 gdb/arch/i386-linux-tdesc.c
 create mode 100644 gdb/arch/i386-linux-tdesc.h
 create mode 100644 gdb/arch/x86-linux-tdesc-features.c
 create mode 100644 gdb/arch/x86-linux-tdesc-features.h
 create mode 100644 gdb/arch/x86-linux-tdesc.h
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h
 delete mode 100644 gdbserver/linux-x86-tdesc.h


base-commit: 16810e455feb26ef826a3ed876d6d7e6d24818b0
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 01/10] gdbserver/ipa/x86: remove unneeded declarations
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 02/10] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
                         ` (11 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Spotted some declarations in gdbserver/linux-amd64-ipa.cc that are no
longer needed.  These are:

  1. 'init_registers_amd64_linux' - the comment claims this function
  is auto generated, but I don't believe that this is still the case.
  Also the function is not used in this file,

  2. 'tdesc_amd64_linux' - this variable doesn't seem to exist any
  more, I suspect this was renamed to 'tdesc_amd64_linux_no_xml', but
  neither are used in this file, so lets remove the declaration.

The amd64 in-process-agent still builds fine after this commit.

There should be no user visible changes after this commit.
---
 gdbserver/linux-amd64-ipa.cc | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 13c8a5bb360..a6346750f49 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -23,10 +23,6 @@
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 
-/* Defined in auto-generated file amd64-linux.c.  */
-void init_registers_amd64_linux (void);
-extern const struct target_desc *tdesc_amd64_linux;
-
 /* fast tracepoints collect registers.  */
 
 #define FT_CR_RIP 0
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 02/10] gdbserver: convert have_ptrace_getregset to a tribool
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 01/10] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 03/10] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
                         ` (10 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, John Baldwin

Convert the have_ptrace_getregset global within gdbserver to a
tribool.  This brings the flag into alignment with the corresponding
flag in GDB.

The gdbserver have_ptrace_getregset variable is already used as a
tribool, it just doesn't have the tribool type.

In a future commit I plan to share more code between GDB and
gdbserver, and having this variable be the same type in both code
bases will make the sharing much easier.

There should be no user visible changes after this commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-arm-low.cc |  6 +++---
 gdbserver/linux-low.cc     |  2 +-
 gdbserver/linux-low.h      |  2 +-
 gdbserver/linux-x86-low.cc | 10 +++++-----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 17b64c09aff..eec4649b235 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -1006,9 +1006,9 @@ arm_target::low_arch_setup ()
 
   /* Check if PTRACE_GETREGSET works.  */
   if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
-    have_ptrace_getregset = 1;
+    have_ptrace_getregset = TRIBOOL_TRUE;
   else
-    have_ptrace_getregset = 0;
+    have_ptrace_getregset = TRIBOOL_FALSE;
 }
 
 bool
@@ -1121,7 +1121,7 @@ arm_target::get_regs_info ()
 {
   const struct target_desc *tdesc = current_process ()->tdesc;
 
-  if (have_ptrace_getregset == 1
+  if (have_ptrace_getregset == TRIBOOL_TRUE
       && (is_aarch32_linux_description (tdesc)
 	  || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
     return &regs_info_aarch32;
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index d8842f0972d..5ba61178132 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -134,7 +134,7 @@ typedef struct
 #endif
 
 /* Does the current host support PTRACE_GETREGSET?  */
-int have_ptrace_getregset = -1;
+enum tribool have_ptrace_getregset = TRIBOOL_UNKNOWN;
 
 /* Return TRUE if THREAD is the leader thread of the process.  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index d34d2738238..eaf87527338 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -951,7 +951,7 @@ void thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid);
 
 bool thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len);
 
-extern int have_ptrace_getregset;
+extern enum tribool have_ptrace_getregset;
 
 /* Search for the value with type MATCH in the auxv vector, with entries of
    length WORDSIZE bytes, of process with pid PID.  If found, store the
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 2532603451a..2603fb2ac5d 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -898,7 +898,7 @@ x86_linux_read_description (void)
       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
 	{
 	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = 0;
+	  have_ptrace_getregset = TRIBOOL_FALSE;
 	  return i386_linux_read_description (X86_XSTATE_X87);
 	}
       else
@@ -917,7 +917,7 @@ x86_linux_read_description (void)
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-  if (have_ptrace_getregset == -1)
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
       struct iovec iov;
@@ -928,10 +928,10 @@ x86_linux_read_description (void)
       /* Check if PTRACE_GETREGSET works.  */
       if (ptrace (PTRACE_GETREGSET, tid,
 		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = 0;
+	have_ptrace_getregset = TRIBOOL_FALSE;
       else
 	{
-	  have_ptrace_getregset = 1;
+	  have_ptrace_getregset = TRIBOOL_TRUE;
 
 	  /* Get XCR0 from XSAVE extended state.  */
 	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
@@ -954,7 +954,7 @@ x86_linux_read_description (void)
     }
 
   /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset
+  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
 		   && (xcr0 & X86_XSTATE_ALL_MASK));
 
   if (xcr0_features)
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 03/10] gdb/x86: move reading of cs and ds state into gdb/nat directory
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 01/10] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 02/10] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 04/10] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
                         ` (9 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, John Baldwin

This patch is part of a series that has the aim sharing the x86 Linux
target description creation code between GDB and gdbserver.

Within GDB part of this process involves reading the cs and ds state
from the 'struct user_regs_struct' using a ptrace call.

This isn't done by gdbserver, which is part of the motivation for this
whole series; the approach gdbserver takes is inferior to the approach
GDB takes (gdbserver relies on reading the file being debugged, and
extracting similar information from the file headers).

This commit moves the reading of cs and ds, which is used to figure
out if a thread is 32-bit or 64-bit (or in x32 mode), into the gdb/nat
directory so that the code can be shared with gdbserver, but at this
point I'm not actually using the code in gdbserver, that will come
later.

As such there should be no user visible changes after this commit, GDB
continues to do things as it did before (reading cs/ds), while
gdbserver continues to use its own approach (which doesn't require
reading cs/ds).

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/nat/x86-linux.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux.h | 28 +++++++++++++++++++++++++++
 gdb/x86-linux-nat.c | 42 +++++-----------------------------------
 3 files changed, 80 insertions(+), 37 deletions(-)

diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index 7a21c8f2c26..ad3ed3c2289 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -19,6 +19,8 @@
 
 #include "x86-linux.h"
 #include "x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include <sys/user.h>
 
 /* Per-thread arch-specific data we want to keep.  */
 
@@ -79,3 +81,48 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp)
 {
   x86_linux_update_debug_registers (lwp);
 }
+
+#ifdef __x86_64__
+/* Value of CS segment register:
+     64bit process: 0x33
+     32bit process: 0x23  */
+#define AMD64_LINUX_USER64_CS 0x33
+
+/* Value of DS segment register:
+     LP64 process: 0x0
+     X32 process: 0x2b  */
+#define AMD64_LINUX_X32_DS 0x2b
+#endif
+
+/* See nat/x86-linux.h.  */
+
+x86_linux_arch_size
+x86_linux_ptrace_get_arch_size (int tid)
+{
+#ifdef __x86_64__
+  unsigned long cs;
+  unsigned long ds;
+
+  /* Get CS register.  */
+  errno = 0;
+  cs = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, cs), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get CS register"));
+
+  bool is_64bit = cs == AMD64_LINUX_USER64_CS;
+
+  /* Get DS register.  */
+  errno = 0;
+  ds = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, ds), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get DS register"));
+
+  bool is_x32 = ds == AMD64_LINUX_X32_DS;
+
+  return x86_linux_arch_size (is_64bit, is_x32);
+#else
+  return x86_linux_arch_size (false, false);
+#endif
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 822882173f9..15153ea277e 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -47,4 +47,32 @@ extern void x86_linux_delete_thread (struct arch_lwp_info *arch_lwp);
 
 extern void x86_linux_prepare_to_resume (struct lwp_info *lwp);
 
+/* Return value from x86_linux_ptrace_get_arch_size function.  Indicates if
+   a thread is 32-bit, 64-bit, or x32.  */
+
+struct x86_linux_arch_size
+{
+  explicit x86_linux_arch_size (bool is_64bit, bool is_x32)
+    : m_is_64bit (is_64bit),
+      m_is_x32 (is_x32)
+  {
+    /* Nothing.  */
+  }
+
+  bool is_64bit () const
+  { return m_is_64bit; }
+
+  bool is_x32 () const
+  { return m_is_x32; }
+
+private:
+  bool m_is_64bit = false;
+  bool m_is_x32 = false;
+};
+
+/* Use ptrace calls to figure out if thread TID is 32-bit, 64-bit, or
+   64-bit running in x32 mode.  */
+
+extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
+
 #endif /* NAT_X86_LINUX_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index a3d8ffb60f1..f91db492d05 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -90,18 +90,6 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
   linux_nat_target::post_startup_inferior (ptid);
 }
 
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
 /* Get Linux/x86 target description from running target.  */
 
 const struct target_desc *
@@ -121,31 +109,11 @@ x86_linux_nat_target::read_description ()
   tid = inferior_ptid.pid ();
 
 #ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  is_64bit = arch_size.is_64bit ();
+  is_x32 = arch_size.is_x32 ();
+
 #elif HAVE_PTRACE_GETFPXREGS
   if (have_ptrace_getfpxregs == -1)
     {
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 04/10] gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (2 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 03/10] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 05/10] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
                         ` (8 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, John Baldwin

This commit is part of a series that aims to share more of the x86
target description reading/generation code between GDB and gdbserver.

There are a huge number of similarities between the code in
gdbserver's x86_linux_read_description function and GDB's
x86_linux_nat_target::read_description function, and it is this
similarity that I plan, in a later commit, to share between GDB and
gdbserver.

However, one thing that is different in x86_linux_read_description is
the code inside the '!use_xml' block.  This is the code that handles
the case where gdbserver is not allowed to send an XML target
description back to GDB.  In this case gdbserver uses some predefined,
fixed, target descriptions.

First, it's worth noting that I suspect this code is not tested any
more.  I couldn't find anything in the testsuite that tries to disable
XML target description support.  And the idea of having a single
"fixed" target description really doesn't work well when we think
about all the various x86 extensions that exist.  Part of me would
like to rip out the no-xml support in gdbserver (at least for x86),
and if a GDB connects that doesn't support XML target descriptions,
gdbserver can just give an error and drop the connection.  GDB has
supported XML target descriptions for 16 years now, I think it would
be reasonable for our shipped gdbserver to drop support for the old
way of doing things.

Anyway.... this commit doesn't do that.

What I did notice was that, over time, the '!use_xml' block appears to
have "drifted" within the x86_linux_read_description function; it's
now not the first check we do.  Instead we make some ptrace calls and
return a target description generated based on the result of these
ptrace calls.  Surely it only makes sense to generate variable target
descriptions if we can send these back to GDB?

So in this commit I propose to move the '!use_xml' block earlier in
the x86_linux_read_description function.

The benefit of this is that this leaves the later half of
x86_linux_read_description much more similar to the GDB function
x86_linux_nat_target::read_description and sets us up for potentially
sharing code between GDB and gdbserver in a later commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-x86-low.cc | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 2603fb2ac5d..eeeddcb9429 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -890,6 +890,22 @@ x86_linux_read_description (void)
 #endif
     }
 
+  /* If we are not allowed to send an XML target description then we need
+     to use the hard-wired target descriptions.  This corresponds to GDB's
+     default machine for x86.
+
+     This check needs to occur before any returns statements that might
+     generate some alternative target descriptions.  */
+  if (!use_xml)
+    {
+#ifdef __x86_64__
+      if (machine == EM_X86_64)
+	return tdesc_amd64_linux_no_xml.get ();
+      else
+#endif
+	return tdesc_i386_linux_no_xml.get ();
+    }
+
 #if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
   if (machine == EM_386 && have_ptrace_getfpxregs == -1)
     {
@@ -906,17 +922,6 @@ x86_linux_read_description (void)
     }
 #endif
 
-  if (!use_xml)
-    {
-      /* Don't use XML.  */
-#ifdef __x86_64__
-      if (machine == EM_X86_64)
-	return tdesc_amd64_linux_no_xml.get ();
-      else
-#endif
-	return tdesc_i386_linux_no_xml.get ();
-    }
-
   if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 05/10] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (3 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 04/10] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 06/10] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
                         ` (7 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Share the definition of I386_LINUX_XSAVE_XCR0_OFFSET between GDB and
gdbserver.

This commit is part of a series that aims to share more of the x86
target description creation code between GDB and gdbserver.  The
I386_LINUX_XSAVE_XCR0_OFFSET #define is used as part of the target
description creation, and I noticed that this constant is defined
separately for GDB and gdbserver.

This commit moves the definition into gdbsupport/x86-xstate.h, which
allows the #define to be shared.

There should be no user visible changes after this commit.
---
 gdb/i386-linux-tdep.h      | 20 --------------------
 gdbserver/linux-x86-low.cc | 21 ---------------------
 gdbsupport/x86-xstate.h    | 20 ++++++++++++++++++++
 3 files changed, 20 insertions(+), 41 deletions(-)

diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 5891747572b..07593c6a8ec 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -58,26 +58,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 /* Return the target description according to XCR0.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */ 
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index eeeddcb9429..e8ef3667eb4 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -831,27 +831,6 @@ x86_target::low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
 \f
 static int use_xml;
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  avx_bytes[576..831]
-	  future_state etc
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 /* Does the current host support the GETFPXREGS request?  The header
    file may or may not define it, and even if it is defined, the
    kernel will return EIO if it's running on a pre-SSE processor.  */
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index 89c1143fbe1..11b37544aa3 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -120,4 +120,24 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
 
 #define I387_MXCSR_INIT_VAL 0x1f80
 
+/* Format of XSAVE extended state is:
+	struct
+	{
+	  fxsave_bytes[0..463]
+	  sw_usable_bytes[464..511]
+	  xstate_hdr_bytes[512..575]
+	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
+	};
+
+  Same memory layout will be used for the coredump NT_X86_XSTATE
+  representing the XSAVE extended state registers.
+
+  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+  extended state mask, which is the same as the extended control register
+  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
+  together with the mask saved in the xstate_hdr_bytes to determine what
+  states the processor/OS supports and what state, used or initialized,
+  the process/thread is in.  */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
 #endif /* COMMON_X86_XSTATE_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 06/10] gdb/gdbserver: share some code relating to target description creation
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (4 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 05/10] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 07/10] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
                         ` (6 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is part of a series to share more of the x86 target
description creation code between GDB and gdbserver.

Unlike previous commits which were mostly refactoring, this commit is
the first that makes a real change, though that change should mostly
be for gdbserver; I've largely adopted the "GDB" way of doing things
for gdbserver, and this fixes a real gdbserver bug.

On a x86-64 Linux target, running the test:

  gdb.server/connect-with-no-symbol-file.exp

results in two core files being created.  Both of these core files are
from the inferior process, created after gdbserver has detached.

In this test a gdbserver process is started and then, after gdbserver
has started, but before GDB attaches, we either delete the inferior
executable, or change its permissions so it can't be read.  Only after
doing this do we attempt to connect with GDB.

As GDB connects to gdbserver, gdbserver attempts to figure out the
target description so that it can send the description to GDB, this
involves a call to x86_linux_read_description.

In x86_linux_read_description one of the first things we do is try to
figure out if the process is 32-bit or 64-bit.  To do this we look up
the executable via the thread-id, and then attempt to read the
architecture size from the executable.  This isn't going to work if
the executable has been deleted, or is no longer readable.

And so, as we can't read the executable, we default to an i386 target
and use an i386 target description.

A consequence of using an i386 target description is that addresses
are assumed to be 32-bits.  Here's an example session that shows the
problems this causes.  This is run on an x86-64 machine, and the test
binary (xx.x) is a standard 64-bit x86-64 binary:

  shell_1$ gdbserver --once localhost :54321 /tmp/xx.x

  shell_2$ gdb -q
  (gdb) set sysroot
  (gdb) shell chmod 000 /tmp/xx.x
  (gdb) target remote :54321
  Remote debugging using :54321
  warning: /tmp/xx.x: Permission denied.
  0xf7fd3110 in ?? ()
  (gdb) show architecture
  The target architecture is set to "auto" (currently "i386").
  (gdb) p/x $pc
  $1 = 0xf7fd3110
  (gdb) info proc mappings
  process 2412639
  Mapped address spaces:

  	Start Addr   End Addr       Size     Offset  Perms   objfile
  	  0x400000   0x401000     0x1000        0x0  r--p   /tmp/xx.x
  	  0x401000   0x402000     0x1000     0x1000  r-xp   /tmp/xx.x
  	  0x402000   0x403000     0x1000     0x2000  r--p   /tmp/xx.x
  	  0x403000   0x405000     0x2000     0x2000  rw-p   /tmp/xx.x
  	0xf7fcb000 0xf7fcf000     0x4000        0x0  r--p   [vvar]
  	0xf7fcf000 0xf7fd1000     0x2000        0x0  r-xp   [vdso]
  	0xf7fd1000 0xf7fd3000     0x2000        0x0  r--p   /usr/lib64/ld-2.30.so
  	0xf7fd3000 0xf7ff3000    0x20000     0x2000  r-xp   /usr/lib64/ld-2.30.so
  	0xf7ff3000 0xf7ffb000     0x8000    0x22000  r--p   /usr/lib64/ld-2.30.so
  	0xf7ffc000 0xf7ffe000     0x2000    0x2a000  rw-p   /usr/lib64/ld-2.30.so
  	0xf7ffe000 0xf7fff000     0x1000        0x0  rw-p
  	0xfffda000 0xfffff000    0x25000        0x0  rw-p   [stack]
  	0xff600000 0xff601000     0x1000        0x0  r-xp   [vsyscall]
  (gdb) info inferiors
    Num  Description       Connection           Executable
  * 1    process 2412639   1 (remote :54321)
  (gdb) shell cat /proc/2412639/maps
  00400000-00401000 r--p 00000000 fd:03 45907133           /tmp/xx.x
  00401000-00402000 r-xp 00001000 fd:03 45907133           /tmp/xx.x
  00402000-00403000 r--p 00002000 fd:03 45907133           /tmp/xx.x
  00403000-00405000 rw-p 00002000 fd:03 45907133           /tmp/xx.x
  7ffff7fcb000-7ffff7fcf000 r--p 00000000 00:00 0          [vvar]
  7ffff7fcf000-7ffff7fd1000 r-xp 00000000 00:00 0          [vdso]
  7ffff7fd1000-7ffff7fd3000 r--p 00000000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7fd3000-7ffff7ff3000 r-xp 00002000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ff3000-7ffff7ffb000 r--p 00022000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffc000-7ffff7ffe000 rw-p 0002a000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  7ffffffda000-7ffffffff000 rw-p 00000000 00:00 0          [stack]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]
  (gdb)

Notice the difference between the mappings reported via GDB and those
reported directly from the kernel via /proc/PID/maps, the addresses of
every mapping is clamped to 32-bits for GDB, while the kernel reports
real 64-bit addresses.

Notice also that the $pc value is a 32-bit value.  It appears to be
within one of the mappings reported by GDB, but is outside any of the
mappings reported from the kernel.

And this is where the problem arises.  When gdbserver detaches from
the inferior we pass the inferior the address from which it should
resume.  Due to the 32/64 bit confusion we tell the inferior to resume
from the 32-bit $pc value, which is not within any valid mapping, and
so, as soon as the inferior resumes, it segfaults.

If we look at how GDB (not gdbserver) figures out its target
description then we see an interesting difference.  GDB doesn't try to
read the executable.  Instead GDB uses ptrace to query the thread's
state, and uses this to figure out the if the thread is 32 or 64 bit.

If we update gdbserver to do it the "GDB" way then the above problem
is resolved, gdbserver now sees the process as 64-bit, and when we
detach from the inferior we give it the correct 64-bit address, and
the inferior no longer segfaults.

Now, I could just update the gdbserver code, but better, I think, to
share one copy of the code between GDB and gdbserver in gdb/nat/.
That is what this commit does.

The cores of x86_linux_read_description from gdbserver and
x86_linux_nat_target::read_description from GDB are moved into a new
file gdb/nat/x86-linux-tdesc.c and combined into a single function
x86_linux_tdesc_for_tid which is called from each location.

This new function does things the GDB way, the only changes are to
allow for the sharing; we now have a callback function to call the
first time that the xcr0 state is read, this allows for GDB and
gdbserver to perform their own initialisation as needed, and
additionally, the new function takes a pointer for where to cache the
xcr0 value, this isn't needed for this commit, but will be useful in a
later commit where gdbserver will want to read this cached xcr0
value.

Another thing to note about this commit is how the functions
i386_linux_read_description and amd64_linux_read_description are
handled.  For now I've left these function as implemented separately
in GDB and gdbserver.  I've moved the declarations of these functions
into gdb/arch/{i386,amd64}-linux-tdesc.h, but the implementations are
left where they are.

A later commit in this series will make these functions shared too,
but doing this is not trivial, so I've left that for a separate
commit.  Merging the declarations as I've done here ensures that
everyone implements the function to the same API, and once these
functions are shared (in a later commit) we'll want a shared
declaration anyway.
---
 gdb/Makefile.in              |   3 +
 gdb/amd64-linux-tdep.c       |   1 +
 gdb/amd64-linux-tdep.h       |   6 --
 gdb/arch/amd64-linux-tdesc.h |  30 +++++++
 gdb/arch/i386-linux-tdesc.h  |  29 +++++++
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |   1 +
 gdb/i386-linux-tdep.h        |   3 -
 gdb/nat/x86-linux-tdesc.c    | 120 ++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  60 ++++++++++++++
 gdb/x86-linux-nat.c          |  91 ++++-----------------
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 +
 gdbserver/linux-i386-ipa.cc  |   1 +
 gdbserver/linux-x86-low.cc   | 151 +++++++++++------------------------
 gdbserver/linux-x86-tdesc.cc |   2 +
 gdbserver/linux-x86-tdesc.h  |   7 --
 17 files changed, 313 insertions(+), 199 deletions(-)
 create mode 100644 gdb/arch/amd64-linux-tdesc.h
 create mode 100644 gdb/arch/i386-linux-tdesc.h
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a9f641c0659..f560ba023bf 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1550,8 +1550,10 @@ HFILES_NO_SRCDIR = \
 	arch/aarch64-insn.h \
 	arch/aarch64-mte-linux.h \
 	arch/aarch64-scalable-linux.h \
+	arch/amd64-linux-tdesc.h \
 	arch/arc.h \
 	arch/arm.h \
+	arch/i386-linux-tdesc.h \
 	arch/i386.h \
 	arch/loongarch.h \
 	arch/ppc-linux-common.h \
@@ -1607,6 +1609,7 @@ HFILES_NO_SRCDIR = \
 	nat/x86-gcc-cpuid.h \
 	nat/x86-linux.h \
 	nat/x86-linux-dregs.h \
+	nat/x86-linux-tdesc.h \
 	python/py-event.h \
 	python/py-events.h \
 	python/py-stopevent.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 9d560ac4fbf..bcb9868e79e 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -41,6 +41,7 @@
 #include "arch/amd64.h"
 #include "target-descriptions.h"
 #include "expop.h"
+#include "arch/amd64-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h
index 2003dcda78f..0ec49e7fe03 100644
--- a/gdb/amd64-linux-tdep.h
+++ b/gdb/amd64-linux-tdep.h
@@ -43,12 +43,6 @@ extern struct target_desc *tdesc_x32_linux;
 extern struct target_desc *tdesc_x32_avx_linux;
 extern struct target_desc *tdesc_x32_avx_avx512_linux;
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  */
-
-const target_desc *amd64_linux_read_description (uint64_t xcr0_features_bit,
-						 bool is_x32);
-
 /* Enum that defines the syscall identifiers for amd64 linux.
    Used for process record/replay, these will be translated into
    a gdb-canonical set of syscall ids in linux-record.c.  */
diff --git a/gdb/arch/amd64-linux-tdesc.h b/gdb/arch/amd64-linux-tdesc.h
new file mode 100644
index 00000000000..db425b60df6
--- /dev/null
+++ b/gdb/arch/amd64-linux-tdesc.h
@@ -0,0 +1,30 @@
+/*  Target description related code for GNU/Linux x86-64.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_AMD64_LINUX_TDESC_H
+#define ARCH_AMD64_LINUX_TDESC_H
+
+struct target_desc;
+
+/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
+
+extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
+							bool is_x32);
+
+#endif /* ARCH_AMD64_LINUX_TDESC_H */
diff --git a/gdb/arch/i386-linux-tdesc.h b/gdb/arch/i386-linux-tdesc.h
new file mode 100644
index 00000000000..0b736337a75
--- /dev/null
+++ b/gdb/arch/i386-linux-tdesc.h
@@ -0,0 +1,29 @@
+/* Target description related code for GNU/Linux i386.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_I386_LINUX_TDESC_H
+#define ARCH_I386_LINUX_TDESC_H
+
+struct target_desc;
+
+/* Return the i386 target description corresponding to XCR0.  */
+
+extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+
+#endif /* ARCH_I386_LINUX_TDESC_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 8b98511cef7..4bcc0696027 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -322,7 +322,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o \
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
 		nat/amd64-linux-siginfo.o"
 		;;
 	    sparc)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 44730f204db..78ebc99d3df 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -39,6 +39,7 @@
 
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
+#include "arch/i386-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 07593c6a8ec..e8691cd778e 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -55,9 +55,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 					   struct ui_out *uiout,
 					   enum gdb_signal siggnal);
 
-/* Return the target description according to XCR0.  */
-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
new file mode 100644
index 00000000000..783a64857fe
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -0,0 +1,120 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "nat/x86-linux-tdesc.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#include "arch/amd64-linux-tdesc.h"
+#endif
+#include "arch/i386.h"
+#include "arch/i386-linux-tdesc.h"
+
+#include "nat/x86-linux.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/x86-xstate.h"
+
+#include <sys/uio.h>
+#include <elf.h>
+
+#ifndef IN_PROCESS_AGENT
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage)
+{
+#ifdef __x86_64__
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  bool is_64bit = arch_size.is_64bit ();
+  bool is_x32 = arch_size.is_x32 ();
+
+  if (sizeof (void *) == 4 && is_64bit && !is_x32)
+    error ("%s", error_msg);
+
+#elif HAVE_PTRACE_GETFPXREGS
+  if (have_ptrace_getfpxregs == -1)
+    {
+      elf_fpxregset_t fpxregs;
+
+      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+	{
+	  have_ptrace_getfpxregs = 0;
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
+	}
+    }
+#endif
+
+  if (*have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    {
+      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, tid,
+		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
+	{
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  *xcr0_storage = 0;
+	}
+      else
+	{
+	  *have_ptrace_getregset = TRIBOOL_TRUE;
+
+	  /* Get XCR0 from XSAVE extended state.  */
+	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+				      / sizeof (uint64_t))];
+
+#ifdef __x86_64__
+	  /* No MPX on x32.  */
+	  if (is_64bit && is_x32)
+	    *xcr0_storage &= ~X86_XSTATE_MPX;
+#endif /* __x86_64__ */
+
+	  xcr0_init_cb (*xcr0_storage);
+	}
+    }
+
+  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
+     PTRACE_GETREGSET is not available then set xcr0_features_bits to
+     zero so that the "no-features" descriptions are returned by the
+     switches below.  */
+  uint64_t xcr0_features_bits;
+  if (*have_ptrace_getregset == TRIBOOL_TRUE)
+    xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+  else
+    xcr0_features_bits = 0;
+
+#ifdef __x86_64__
+  if (is_64bit)
+    {
+      return amd64_linux_read_description (xcr0_features_bits, is_x32);
+    }
+  else
+#endif
+    return i386_linux_read_description (xcr0_features_bits);
+}
+
+#endif /* !IN_PROCESS_AGENT */
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
new file mode 100644
index 00000000000..8c481609876
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -0,0 +1,60 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_X86_LINUX_TDESC_H
+#define NAT_X86_LINUX_TDESC_H
+
+#include "gdbsupport/function-view.h"
+
+struct target_desc;
+
+/* Return the target description for Linux thread TID.
+
+   When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
+   xcr0 is read using ptrace calls and stored into *XCR0_STORAGE.  Then
+   XCR0_INIT_CB is called with the value of *XCR0_STORAGE and
+   *HAVE_PTRACE_GETREGSET is set to TRIBOOL_TRUE.
+
+   If the attempt to read xcr0 using ptrace fails then *XCR0_STORAGE is set
+   to zero and *HAVE_PTRACE_GETREGSET is set to TRIBOOL_FALSE.
+
+   The storage pointed to by XCR0_STORAGE must exist until the program
+   terminates, this storage is used to cache the xcr0 value.  As such
+   XCR0_INIT_CB will only be called once if xcr0 is successfully read using
+   ptrace, or not at all if the ptrace call fails.
+
+   This function returns a target description based on the extracted xcr0
+   value along with other characteristics of the thread identified by TID.
+
+   This function can return nullptr if we encounter a machine configuration
+   for which a target_desc cannot be created.  Ideally this would not be
+   the case, we should be able to create a target description for every
+   possible machine configuration.  See amd64_linux_read_description and
+   i386_linux_read_description for cases when nullptr might be
+   returned.
+
+   ERROR_MSG is using in an error() call if we try to create a target
+   description for a 64-bit process but this is a 32-bit build of GDB.  */
+
+extern const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage);
+
+#endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index f91db492d05..57424fe7c3a 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -41,6 +41,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "nat/linux-ptrace.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* linux_nat_target::low_new_fork implementation.  */
 
@@ -95,90 +96,26 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
 const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
+  static uint64_t xcr0_storage;
 
   if (inferior_ptid == null_ptid)
     return this->beneath ()->read_description ();
 
-  tid = inferior_ptid.pid ();
-
-#ifdef __x86_64__
-
-  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
-  is_64bit = arch_size.is_64bit ();
-  is_x32 = arch_size.is_x32 ();
-
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
-	}
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  m_xsave_layout = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset == TRIBOOL_TRUE)
-    xcr0_features_bits = xcr0 & X86_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      return amd64_linux_read_description (xcr0_features_bits, is_x32);
-#endif
-    }
-  else
-    {
-      const struct target_desc * tdesc
-	= i386_linux_read_description (xcr0_features_bits);
+  int tid = inferior_ptid.pid ();
 
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE_MASK);
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDB");
 
-      return tdesc;
-    }
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state.  */
+  auto cb = [&] (uint64_t xcr0)
+  {
+    this->m_xsave_layout
+      = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+  };
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb,
+				  error_msg, &xcr0_storage);
 }
 \f
 
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 9e861a75088..7a2702d78bf 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -109,6 +109,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -371,6 +372,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index a6346750f49..df5e6aca081 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -22,6 +22,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "arch/amd64-linux-tdesc.h"
 
 /* fast tracepoints collect registers.  */
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 8f14e0937d4..aa346fc9bc3 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -22,6 +22,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "arch/i386-linux-tdesc.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index e8ef3667eb4..37c15aa688b 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -29,8 +29,11 @@
 
 #ifdef __x86_64__
 #include "nat/amd64-linux-siginfo.h"
+#include "arch/amd64-linux-tdesc.h"
 #endif
 
+#include "arch/i386-linux-tdesc.h"
+
 #include "gdb_proc_service.h"
 /* Don't include elf/common.h if linux/elf.h got included by
    gdb_proc_service.h.  */
@@ -46,6 +49,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "linux-x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
 static target_desc_up tdesc_amd64_linux_no_xml;
@@ -842,32 +846,20 @@ int have_ptrace_getfpxregs =
 #endif
 ;
 
+/* Cached xcr0 value.  This is initialised the first time
+   x86_linux_read_description is called.  */
+
+static uint64_t xcr0_storage;
+
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
 x86_linux_read_description (void)
 {
-  unsigned int machine;
-  int is_elf64;
-  int xcr0_features;
-  int tid;
-  static uint64_t xcr0;
-  static int xsave_len;
-  struct regset_info *regset;
-
-  tid = lwpid_of (current_thread);
-
-  is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+  int tid = lwpid_of (current_thread);
 
-  if (sizeof (void *) == 4)
-    {
-      if (is_elf64 > 0)
-       error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
-      else if (machine == EM_X86_64)
-       error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
-    }
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDBserver");
 
   /* If we are not allowed to send an XML target description then we need
      to use the hard-wired target descriptions.  This corresponds to GDB's
@@ -877,103 +869,54 @@ x86_linux_read_description (void)
      generate some alternative target descriptions.  */
   if (!use_xml)
     {
+      x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+      bool is_64bit = arch_size.is_64bit ();
+      bool is_x32 = arch_size.is_x32 ();
+
+      if (sizeof (void *) == 4 && is_64bit && !is_x32)
+	error ("%s", error_msg);
+
 #ifdef __x86_64__
-      if (machine == EM_X86_64)
+      if (is_64bit && !is_x32)
 	return tdesc_amd64_linux_no_xml.get ();
       else
 #endif
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
-  if (machine == EM_386 && have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87);
-	}
-      else
-	have_ptrace_getfpxregs = 1;
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  /* No MPX on x32.  */
-	  if (machine == EM_X86_64 && !is_elf64)
-	    xcr0 &= ~X86_XSTATE_MPX;
-
-	  xsave_len = x86_xsave_length ();
-
-	  /* Use PTRACE_GETREGSET if it is available.  */
-	  for (regset = x86_regsets;
-	       regset->fill_function != NULL; regset++)
-	    if (regset->get_request == PTRACE_GETREGSET)
-	      regset->size = xsave_len;
-	    else if (regset->type != GENERAL_REGS)
-	      regset->size = 0;
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
-		   && (xcr0 & X86_XSTATE_ALL_MASK));
-
-  if (xcr0_features)
-    i387_set_xsave_mask (xcr0, xsave_len);
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [] (uint64_t xcr0)
+  {
+    i387_set_xsave_mask (xcr0, x86_xsave_length ());
+  };
 
-  if (machine == EM_X86_64)
-    {
-#ifdef __x86_64__
-      const target_desc *tdesc = NULL;
+  /* If have_ptrace_getregset is changed to true by calling
+     x86_linux_tdesc_for_tid then we will perform some additional
+     initialisation.  */
+  bool have_ptrace_getregset_is_unknown
+    = have_ptrace_getregset == TRIBOOL_UNKNOWN;
 
-      if (xcr0_features)
-	{
-	  tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
-						!is_elf64);
-	}
+  const target_desc *tdesc
+    = x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb, error_msg,
+			       &xcr0_storage);
 
-      if (tdesc == NULL)
-	tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
-      return tdesc;
-#endif
-    }
-  else
+  if (have_ptrace_getregset_is_unknown
+      && have_ptrace_getregset == TRIBOOL_TRUE)
     {
-      const target_desc *tdesc = NULL;
-
-      if (xcr0_features)
-	  tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
-
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE);
-
-      return tdesc;
+      int xsave_len = x86_xsave_length ();
+
+      /* Use PTRACE_GETREGSET if it is available.  */
+      for (regset_info *regset = x86_regsets;
+	   regset->fill_function != nullptr;
+	   regset++)
+	if (regset->get_request == PTRACE_GETREGSET)
+	  regset->size = xsave_len;
+	else if (regset->type != GENERAL_REGS)
+	  regset->size = 0;
     }
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return tdesc;
 }
 
 /* Update all the target description of all processes; a new GDB
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index cd3b5d80e37..af3735aa895 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -23,8 +23,10 @@
 #include "gdbsupport/x86-xstate.h"
 #ifdef __x86_64__
 #include "arch/amd64.h"
+#include "arch/amd64-linux-tdesc.h"
 #endif
 #include "x86-tdesc.h"
+#include "arch/i386-linux-tdesc.h"
 
 /* Return the right x86_linux_tdesc index for a given XCR0.  Return
    X86_TDESC_LAST if can't find a match.  */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index f9561b129ae..576aaf5e165 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -46,11 +46,4 @@ int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
 
 const struct target_desc *i386_get_ipa_tdesc (int idx);
 
-#ifdef __x86_64__
-const struct target_desc *amd64_linux_read_description (uint64_t xcr0,
-							bool is_x32);
-#endif
-
-const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 07/10] gdb/arch: assert that X86_XSTATE_MPX is not set for x32
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (5 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 06/10] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 08/10] gdbserver: update target description creation for x86/linux Andrew Burgess
                         ` (5 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

While rebasing this series past this commit:

  commit 4bb20a6244b7091a9a7a2ae35dfbd7e8db27550a
  Date:   Wed Mar 20 04:13:18 2024 -0700

      gdbserver: Clear X86_XSTATE_MPX bits in xcr0 on x32

I worried that there could be other paths that might result in an xcr0
value which has X86_XSTATE_MPX set in x32 mode.  As everyone
eventually calls amd64_create_target_description to build their target
description, I figured we could assert in here that if X86_XSTATE_MPX
is set then we should not be an x32 target, this will uncover any
other bugs in this area.

I'm not currently able to build/run any x32 binaries, so I have no way
to test this, but the author of commit 4bb20a6244b7091 did test this
series with that assert in place and didn't see any problems.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31511
---
 gdb/arch/amd64.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index cb9683c6931..94d55d72a4e 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -65,8 +65,12 @@ amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
   if (xcr0 & X86_XSTATE_AVX)
     regnum = create_feature_i386_64bit_avx (tdesc.get (), regnum);
 
-  if ((xcr0 & X86_XSTATE_MPX) && !is_x32)
-    regnum = create_feature_i386_64bit_mpx (tdesc.get (), regnum);
+  if (xcr0 & X86_XSTATE_MPX)
+    {
+      /* MPX is not available on x32.  */
+      gdb_assert (!is_x32);
+      regnum = create_feature_i386_64bit_mpx (tdesc.get (), regnum);
+    }
 
   if (xcr0 & X86_XSTATE_AVX512)
     regnum = create_feature_i386_64bit_avx512 (tdesc.get (), regnum);
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 08/10] gdbserver: update target description creation for x86/linux
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (6 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 07/10] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 09/10] gdb: move xcr0 == 0 check into i386_linux_core_read_description Andrew Burgess
                         ` (4 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, John Baldwin

This commit is part of a series which aims to share more of the target
description creation between GDB and gdbserver for x86/Linux.

After some refactoring earlier in this series the shared
x86_linux_tdesc_for_tid function was added into nat/x86-linux-tdesc.c.
However, this function still relies on amd64_linux_read_description
and i386_linux_read_description which are implemented separately for
both gdbserver and GDB.  Given that at their core, all these functions
do is:

  1. take an xcr0 value as input,
  2. mask out some feature bits,
  3. look for a cached pre-generated target description and return it
     if found,
  4. if no cached target description is found then call either
     amd64_create_target_description or
     i386_create_target_description to create a new target
     description, which is then added to the cache.  Return the newly
     created target description.

The inner functions amd64_create_target_description and
i386_create_target_description are already shared between GDB and
gdbserver (in the gdb/arch/ directory), so the only thing that
the *_read_description functions really do is add the caching layer,
and it feels like this really could be shared.

However, we have a small problem.

On the GDB side we create target descriptions using a different set of
cpu features than on the gdbserver side!  This means that for the
exact same target, we might get a different target description when
using native GDB vs using gdbserver.  This surely feels like a
mistake, I would expect to get the same target description on each.

The table below shows the number of possible different target
descriptions that we can create on the GDB side vs on the gdbserver
side for each target type:

        | GDB | gdbserver
  ------|-----|----------
  i386  | 64  | 7
  amd64 | 32  | 7
  x32   | 16  | 7

So in theory, all I want to do is move the GDB version
of *_read_description into the arch/ directory and have gdbserver use
that, then both GDB and gdbserver would be able to create any of the
possible target descriptions.

Unfortunately it's a little more complex than that due to the in
process agent (IPA).

When the IPA is in use, gdbserver sends a target description index to
the IPA, and the IPA uses this to find the correct target description
to use.

** START OF AN ASIDE **

Back in the day I suspect this approach made perfect sense.  However
since this commit:

  commit a8806230241d201f808d856eaae4d44088117b0c
  Date:   Thu Dec 7 17:07:01 2017 +0000

      Initialize target description early in IPA

I think passing the index is now more trouble than its worth.

We used to pass the index, and then use that index to lookup which
target description to instantiate and use.  However, the above commit
fixed an issue where we can't call malloc() within (certain parts of)
the IPA (apparently), so instead we now pre-compute _every_ possible
target description within the IPA.  The index is now only used to
lookup which of the (many) pre-computed target descriptions to use.

It would (I think) have been easier all around if the IPA just
self-inspected, figured out its own xcr0 value, and used that to
create the one target description that is required.  So long as the
xcr0 to target description code is shared (at compile time) with
gdbserver, then we can be sure that the IPA will derive the same
target description as gdbserver, and we would avoid all this index
passing business, which has made this commit so very, very painful.

** END OF AN ASIDE **

Currently then for x86/linux, gdbserver sends a number between 0 and 7
to the IPA, and the IPA uses this to create a target description.

However, I am proposing that gdbserver should now create one of (up
to) 64 different target descriptions for i386, so this 0 to 7 index
isn't going to be good enough any more (amd64 and x32 have slightly
fewer possible target descriptions, but still more than 8, so the
problem is the same).

For a while I wondered if I was going to have to try and find some
backward compatible solution for this mess.  But after seeing how
lightly the IPA is actually documented, I wonder if it is not the case
that there is a tight coupling between a version of gdbserver and a
version of the IPA?  At least I'm hoping so.

In this commit I have thrown out the old IPA target description index
numbering scheme, and switched to a completely new numbering scheme.
Instead of the index that is passed being arbitrary, the index is
instead calculated from the set of cpu features that are present on
the target.  Within the IPA we can then reverse this logic to recreate
the xcr0 value based on the index, and from the xcr0 value we can
choose the correct target description.

With the gdbserver to IPA numbering scheme issue resolved I have then
update the gdbserver versions of amd64_linux_read_description and
i386_linux_read_description so that they create target descriptions
using the same set of cpu features as GDB itself.

After this gdbserver should now always come up with the same target
description as GDB does on any x86/Linux target.

This commit does not introduce any new code sharing between GDB and
gdbserver as previous commits in this series have done.  Instead this
commit is all about bringing GDB and gdbserver into alignment
functionally so that the next commit(s) can merge the GDB and
gdbserver versions of these functions.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-amd64-ipa.cc |  43 +----
 gdbserver/linux-i386-ipa.cc  |  23 +--
 gdbserver/linux-x86-low.cc   |  15 +-
 gdbserver/linux-x86-tdesc.cc | 316 +++++++++++++++++++++++++----------
 gdbserver/linux-x86-tdesc.h  |  49 +++---
 5 files changed, 278 insertions(+), 168 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index df5e6aca081..0c80812cc6f 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -164,47 +164,21 @@ supply_static_tracepoint_registers (struct regcache *regcache,
 
 #endif /* HAVE_UST */
 
-#if !defined __ILP32__
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-#endif
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
 
 #if defined __ILP32__
-  switch (idx)
-    {
-    case X86_TDESC_SSE:
-      return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-    case X86_TDESC_AVX:
-      return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-    case X86_TDESC_AVX_AVX512:
-      return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
-    default:
-      break;
-    }
+  bool is_x32 = true;
 #else
-  return amd64_linux_read_description (idx2mask[idx], false);
+  bool is_x32 = false;
 #endif
 
-  internal_error ("unknown ipa tdesc index: %d", idx);
+  return amd64_linux_read_description (xcr0, is_x32);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a
@@ -272,11 +246,10 @@ void
 initialize_low_tracepoint (void)
 {
 #if defined __ILP32__
-  amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+  for (auto i = 0; i < x86_linux_x32_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
 #else
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    amd64_linux_read_description (idx2mask[i], false);
+  for (auto i = 0; i < x86_linux_amd64_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
 #endif
 }
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index aa346fc9bc3..c1c3152fb04 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -245,28 +245,15 @@ initialize_fast_tracepoint_trampoline_buffer (void)
     }
 }
 
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
-  return i386_linux_read_description (idx2mask[idx]);
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+
+  return i386_linux_read_description (xcr0);
 }
 
 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
@@ -288,6 +275,6 @@ void
 initialize_low_tracepoint (void)
 {
   initialize_fast_tracepoint_trampoline_buffer ();
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    i386_linux_read_description (idx2mask[i]);
+  for (auto i = 0; i < x86_linux_i386_tdesc_count (); i++)
+    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
 }
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 37c15aa688b..4f473730efb 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -2893,14 +2893,17 @@ x86_target::get_ipa_tdesc_idx ()
   struct regcache *regcache = get_thread_regcache (current_thread, 0);
   const struct target_desc *tdesc = regcache->tdesc;
 
+  if (!use_xml)
+    {
+      if (tdesc == tdesc_i386_linux_no_xml.get ()
 #ifdef __x86_64__
-  return amd64_get_ipa_tdesc_idx (tdesc);
-#endif
-
-  if (tdesc == tdesc_i386_linux_no_xml.get ())
-    return X86_TDESC_SSE;
+	  || tdesc == tdesc_amd64_linux_no_xml.get ()
+#endif /* __x86_64__ */
+	  )
+	return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+    }
 
-  return i386_get_ipa_tdesc_idx (tdesc);
+  return x86_linux_xcr0_to_tdesc_idx (xcr0_storage);
 }
 
 /* The linux target ops object.  */
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index af3735aa895..5e12526bf17 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -28,96 +28,278 @@
 #include "x86-tdesc.h"
 #include "arch/i386-linux-tdesc.h"
 
-/* Return the right x86_linux_tdesc index for a given XCR0.  Return
-   X86_TDESC_LAST if can't find a match.  */
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
 
-static enum x86_linux_tdesc
-xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
 {
-  if (xcr0 & X86_XSTATE_PKRU)
-    {
-      if (is_x32)
-	{
-	  /* No x32 MPX and PKU, fall back to avx_avx512.  */
-	  return X86_TDESC_AVX_AVX512;
-	}
-      else
-	return X86_TDESC_AVX_MPX_AVX512_PKU;
-    }
-  else if (xcr0 & X86_XSTATE_AVX512)
-    return X86_TDESC_AVX_AVX512;
-  else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count_1 ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count_1 ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count_1 ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_AVX_MPX;
+     if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
+	  == x86_linux_all_tdesc_features[i].feature)
+	idx |= (1 << i);
     }
-  else if (xcr0 & X86_XSTATE_MPX)
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_MPX;
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
     }
-  else if (xcr0 & X86_XSTATE_AVX)
-    return X86_TDESC_AVX;
-  else if (xcr0 & X86_XSTATE_SSE)
-    return X86_TDESC_SSE;
-  else if (xcr0 & X86_XSTATE_X87)
-    return X86_TDESC_MMX;
-  else
-    return X86_TDESC_LAST;
+
+  return xcr0;
 }
 
+#endif /* IN_PROCESS_AGENT */
+
 #if defined __i386__ || !defined IN_PROCESS_AGENT
 
-static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible i386 target descriptions.  */
 
-/* Return the target description according to XCR0.  */
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count_1 ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 i386_linux_read_description (uint64_t xcr0)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count_1 ());
 
-  struct target_desc **tdesc = &i386_tdescs[idx];
+  target_desc **tdesc = &i386_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = i386_create_target_description (xcr0, true, false);
 
       init_target_desc (*tdesc, i386_expedite_regs);
     }
 
-  return *tdesc;;
+  return *tdesc;
 }
 #endif
 
 #ifdef __x86_64__
 
-static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
-static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count_1 ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count_1 ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count_1 ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count_1 ());
 
-  struct target_desc **tdesc = NULL;
+  target_desc **tdesc = nullptr;
 
   if (is_x32)
     tdesc = &x32_tdescs[idx];
   else
     tdesc = &amd64_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
 
@@ -127,39 +309,3 @@ amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 }
 
 #endif
-
-#ifndef IN_PROCESS_AGENT
-
-int
-i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == i386_tdescs[i])
-	return i;
-    }
-
-  /* If none tdesc is found, return the one with minimum features.  */
-  return X86_TDESC_MMX;
-}
-
-#if defined __x86_64__
-int
-amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == amd64_tdescs[i])
-	return i;
-    }
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == x32_tdescs[i])
-	return i;
-    }
-
-  return X86_TDESC_SSE;
-}
-
-#endif
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index 576aaf5e165..3d6e0e51833 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -21,29 +21,30 @@
 #ifndef GDBSERVER_LINUX_X86_TDESC_H
 #define GDBSERVER_LINUX_X86_TDESC_H
 
-/* Note: since IPA obviously knows what ABI it's running on (i386 vs x86_64
-   vs x32), it's sufficient to pass only the register set here.  This,
-   together with the ABI known at IPA compile time, maps to a tdesc.  */
-
-enum x86_linux_tdesc {
-  X86_TDESC_MMX = 0,
-  X86_TDESC_SSE = 1,
-  X86_TDESC_AVX = 2,
-  X86_TDESC_MPX = 3,
-  X86_TDESC_AVX_MPX = 4,
-  X86_TDESC_AVX_AVX512 = 5,
-  X86_TDESC_AVX_MPX_AVX512_PKU = 6,
-  X86_TDESC_LAST = 7,
-};
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-int i386_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-#if defined __x86_64__ && !defined IN_PROCESS_AGENT
-int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-const struct target_desc *i386_get_ipa_tdesc (int idx);
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_tdesc_count ();
+extern int x86_linux_x32_tdesc_count ();
+extern int x86_linux_i386_tdesc_count ();
+
+
+#endif /* IN_PROCESS_AGENT */
 
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 09/10] gdb: move xcr0 == 0 check into i386_linux_core_read_description
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (7 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 08/10] gdbserver: update target description creation for x86/linux Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-05 12:33       ` [PATCHv4 10/10] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
                         ` (3 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Currently, in i386_linux_core_read_description, if GDB fails to
extract an xcr0 value from the core file, then we will have a default
zero value for the xcr0 variable, we still call the
i386_linux_read_description function, which checks for this zero value
and returns nullptr.

Back in i386_linux_core_read_description we spot the nullptr return
value from i386_linux_read_description and call
i386_linux_read_description again, but this time passing a default
value for xcr0.

In the next commit I plan to rework i386_linux_read_description, and
in so doing I will remove the check for xcr0 == 0, this is inline with
how the amd64 code is written.

However, this means that the 'xcr0 == 0' check needs to move up the
stack to i386_linux_core_read_description, again, this brings the i386
code into line with the amd64 code.

This is just a refactor in preparation for the next commit, there
should be no user visible changes after this commit.
---
 gdb/i386-linux-tdep.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 78ebc99d3df..c796f87780b 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -714,15 +714,16 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
   /* Linux/i386.  */
   x86_xsave_layout layout;
   uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
-  const struct target_desc *tdesc = i386_linux_read_description (xcr0);
 
-  if (tdesc != NULL)
-    return tdesc;
+  if (xcr0 == 0)
+    {
+      if (bfd_get_section_by_name (abfd, ".reg-xfp") != nullptr)
+	xcr0 = X86_XSTATE_SSE_MASK;
+      else
+	xcr0 = X86_XSTATE_X87_MASK;
+    }
 
-  if (bfd_get_section_by_name (abfd, ".reg-xfp") != NULL)
-    return i386_linux_read_description (X86_XSTATE_SSE_MASK);
-  else
-    return i386_linux_read_description (X86_XSTATE_X87_MASK);
+  return i386_linux_read_description (xcr0);
 }
 
 /* Similar to i386_supply_fpregset, but use XSAVE extended state.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 10/10] gdb/gdbserver: share x86/linux tdesc caching
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (8 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 09/10] gdb: move xcr0 == 0 check into i386_linux_core_read_description Andrew Burgess
@ 2024-04-05 12:33       ` Andrew Burgess
  2024-04-09 18:37       ` [PATCHv4 00/10] x86/Linux Target Description Changes John Baldwin
                         ` (2 subsequent siblings)
  12 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-05 12:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit builds on the previous series of commits to share the
target description caching code between GDB and gdbserver for
x86/Linux targets.

The objective of this commit is to move the four functions (2 each of)
i386_linux_read_description and amd64_linux_read_description into the
gdb/arch/ directory and combine them so we have just a single copy of
each.  Then GDB, gdbserver, and the in-process-agent (IPA) will link
against these shared functions.

It is worth reading the description of the previous commit(s) to see
why this merging is not as simple as it seems, but in summary,
gdbserver used to generate a different set of possible target
descriptions than GDB.  The previous commit(s) fixed this, so now it
should be simpler to share the functions.

One curiosity with this patch is the function
x86_linux_post_init_tdesc.  On the gdbserver side the two functions
amd64_linux_read_description and i386_linux_read_description have some
functionality that is not present on the GDB side, that is some
additional configuration that is performed as each target description
is created to setup the expedited registers.

To support this I've added the function x86_linux_post_init_tdesc.
This function is called from the two *_linux_read_description
functions, but is implemented separately for GDB and gdbserver.

An alternative approach that avoids adding x86_linux_post_init_tdesc
would be to have x86_linux_tdesc_for_tid return a non-const target
description, in x86_target::low_arch_setup we could then inspect the
target description to figure out if it is 64-bit or not, and modify
the target description as needed.  In the end I figured that adding
the x86_linux_post_init_tdesc function was good enough, so went with
that solution.

The contents of gdbserver/linux-x86-low.cc have moved to
gdb/arch/x86-linux-tdesc-features.c, and gdbserver/linux-x86-tdesc.h
has moved to gdb/arch/x86-linux-tdesc-features.h, this change leads to
some updates in the #includes in the gdbserver/ directory.

For testing I've done the following:

  - Built on x86-64 GNU/Linux for all targets, and just for the native
    target,

  - Build on i386 GNU/Linux for all targets, and just for the native
    target,

  - Build on a 64-bit, non-x86 GNU/Linux for all targets, just for the
    native target, and for targets x86_64-*-linux and i386-*-linux.

This did flush out a bunch of build issues where I'd failed to add a
required file in a configure.* file, but I think everything should now
be good.
---
 gdb/Makefile.in                               |   5 +
 gdb/amd64-linux-tdep.c                        |  31 --
 gdb/arch/amd64-linux-tdesc.c                  |  61 ++++
 gdb/arch/i386-linux-tdesc.c                   |  51 +++
 gdb/arch/x86-linux-tdesc-features.c           | 247 +++++++++++++++
 .../arch/x86-linux-tdesc-features.h           |  50 +--
 gdb/arch/x86-linux-tdesc.h                    |  37 +++
 gdb/configure.nat                             |   6 +-
 gdb/configure.tgt                             |  11 +-
 gdb/i386-linux-tdep.c                         |  26 +-
 gdbserver/configure.srv                       |   9 +
 gdbserver/linux-amd64-ipa.cc                  |   2 +-
 gdbserver/linux-i386-ipa.cc                   |   2 +-
 gdbserver/linux-x86-low.cc                    |   2 +-
 gdbserver/linux-x86-tdesc.cc                  | 292 +-----------------
 15 files changed, 469 insertions(+), 363 deletions(-)
 create mode 100644 gdb/arch/amd64-linux-tdesc.c
 create mode 100644 gdb/arch/i386-linux-tdesc.c
 create mode 100644 gdb/arch/x86-linux-tdesc-features.c
 rename gdbserver/linux-x86-tdesc.h => gdb/arch/x86-linux-tdesc-features.h (52%)
 create mode 100644 gdb/arch/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f560ba023bf..9dc84cc8f0c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -748,6 +748,7 @@ ALL_64_TARGET_OBS = \
 	arch/aarch64-insn.o \
 	arch/aarch64-mte-linux.o \
 	arch/aarch64-scalable-linux.o \
+	arch/amd64-linux-tdesc.o \
 	arch/amd64.o \
 	arch/riscv.o \
 	bpf-tdep.o \
@@ -788,9 +789,11 @@ ALL_TARGET_OBS = \
 	arch/arm.o \
 	arch/arm-get-next-pcs.o \
 	arch/arm-linux.o \
+	arch/i386-linux-tdesc.o \
 	arch/i386.o \
 	arch/loongarch.o \
 	arch/ppc-linux-common.o \
+	arch/x86-linux-tdesc-features.o \
 	arm-bsd-tdep.o \
 	arm-fbsd-tdep.o \
 	arm-linux-tdep.o \
@@ -1559,6 +1562,8 @@ HFILES_NO_SRCDIR = \
 	arch/ppc-linux-common.h \
 	arch/ppc-linux-tdesc.h \
 	arch/riscv.h \
+	arch/x86-linux-tdesc-features.h \
+	arch/x86-linux-tdesc.h \
 	cli/cli-cmds.h \
 	cli/cli-decode.h \
 	cli/cli-script.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index bcb9868e79e..c707745cd9a 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1577,37 +1577,6 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
-const target_desc *
-amd64_linux_read_description (uint64_t xcr0_features_bit, bool is_x32)
-{
-  static target_desc *amd64_linux_tdescs \
-    [2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  static target_desc *x32_linux_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/] = {};
-
-  target_desc **tdesc;
-
-  if (is_x32)
-    {
-      tdesc = &x32_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0 ]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-  else
-    {
-      tdesc = &amd64_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_MPX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-
-  if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0_features_bit, is_x32,
-					      true, true);
-
-  return *tdesc;
-}
-
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
diff --git a/gdb/arch/amd64-linux-tdesc.c b/gdb/arch/amd64-linux-tdesc.c
new file mode 100644
index 00000000000..63b5ddfcece
--- /dev/null
+++ b/gdb/arch/amd64-linux-tdesc.c
@@ -0,0 +1,61 @@
+/* Target description related code for GNU/Linux x86-64.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc.h"
+#include "arch/amd64-linux-tdesc.h"
+#include "arch/amd64.h"
+#include "arch/x86-linux-tdesc-features.h"
+
+
+/* See arch/amd64-linux-tdesc.h.  */
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, 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.  */
+  static tdesc_cache_type amd64_tdesc_cache, x32_tdesc_cache;
+
+  tdesc_cache_type &tdesc_cache = is_x32 ? x32_tdesc_cache : amd64_tdesc_cache;
+
+  xcr0 &= is_x32
+    ? x86_linux_x32_tdesc_feature_mask ()
+    : x86_linux_amd64_tdesc_feature_mask ();
+
+  const auto it = tdesc_cache.find (xcr0);
+  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));
+  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));
+  return ptr;
+}
diff --git a/gdb/arch/i386-linux-tdesc.c b/gdb/arch/i386-linux-tdesc.c
new file mode 100644
index 00000000000..6d1f1793457
--- /dev/null
+++ b/gdb/arch/i386-linux-tdesc.c
@@ -0,0 +1,51 @@
+/* Target description related code for GNU/Linux i386.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc.h"
+#include "arch/i386-linux-tdesc.h"
+#include "arch/i386.h"
+#include "arch/x86-linux-tdesc-features.h"
+
+/* See arch/i386-linux-tdesc.h.  */
+
+const target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+  /* 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.  */
+  static std::unordered_map<uint64_t, const target_desc_up> i386_tdesc_cache;
+
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+
+  const auto it = i386_tdesc_cache.find (xcr0);
+  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));
+  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));
+  return ptr;
+}
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
new file mode 100644
index 00000000000..91abd9b1928
--- /dev/null
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -0,0 +1,247 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc-features.h"
+
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
+
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  return x86_linux_amd64_tdesc_feature_mask_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  return x86_linux_x32_tdesc_feature_mask_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_i386_tdesc_feature_mask ()
+{
+  return x86_linux_i386_tdesc_feature_mask_1 ();
+}
+
+#ifdef GDBSERVER
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
+	  == x86_linux_all_tdesc_features[i].feature)
+	idx |= (1 << i);
+    }
+
+  return idx;
+}
+
+#endif /* GDBSERVER */
+
+#ifdef IN_PROCESS_AGENT
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_amd64_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_x32_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_i386_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
+    }
+
+  return xcr0;
+}
+
+#endif /* IN_PROCESS_AGENT */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdb/arch/x86-linux-tdesc-features.h
similarity index 52%
rename from gdbserver/linux-x86-tdesc.h
rename to gdb/arch/x86-linux-tdesc-features.h
index 3d6e0e51833..0d3db587174 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdb/arch/x86-linux-tdesc-features.h
@@ -1,7 +1,6 @@
-/* Low level support for x86 (i386 and x86-64), shared between gdbserver
-   and IPA.
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
 
-   Copyright (C) 2016-2024 Free Software Foundation, Inc.
+   Copyright (C) 2024 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -18,33 +17,46 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef GDBSERVER_LINUX_X86_TDESC_H
-#define GDBSERVER_LINUX_X86_TDESC_H
+#ifndef ARCH_X86_LINUX_TDESC_FEATURES_H
+#define ARCH_X86_LINUX_TDESC_FEATURES_H
 
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+#include "gdbsupport/x86-xstate.h"
+#include "gdbsupport/gdb_assert.h"
 
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+/* Return a mask of X86_STATE_* feature flags.  The returned mask indicates
+   the set of features which are checked for when creating the target
+   description for each of amd64, x32, and i386.  */
 
-#ifdef IN_PROCESS_AGENT
+extern uint64_t x86_linux_amd64_tdesc_feature_mask ();
+extern uint64_t x86_linux_x32_tdesc_feature_mask ();
+extern uint64_t x86_linux_i386_tdesc_feature_mask ();
 
-/* 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.  */
+#ifdef GDBSERVER
 
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+/* Convert an xcr0 value into an integer.  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.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
 
-/* Within the in-process-agent we need to pre-initialise all of the target
-   descriptions, to do this we need to know how many target descriptions
-   there are for each different target type.  These functions return the
-   target description count for the relevant target.  */
+#endif /* GDBSERVER */
+
+#ifdef IN_PROCESS_AGENT
+
+/* Return the maximum possible number of target descriptions for each of
+   amd64, x32, and i386.  These are used by the in-process-agent to
+   generate every possible target description.  */
 
 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
 
 #endif /* IN_PROCESS_AGENT */
 
-#endif /* GDBSERVER_LINUX_X86_TDESC_H */
+#endif /* ARCH_X86_LINUX_TDESC_FEATURES_H */
diff --git a/gdb/arch/x86-linux-tdesc.h b/gdb/arch/x86-linux-tdesc.h
new file mode 100644
index 00000000000..152592fcf76
--- /dev/null
+++ b/gdb/arch/x86-linux-tdesc.h
@@ -0,0 +1,37 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_X86_LINUX_TDESC_H
+#define ARCH_X86_LINUX_TDESC_H
+
+struct target_desc;
+
+/* This function is called from amd64_linux_read_description and
+   i386_linux_read_description after a new target description has been
+   created, TDESC is the new target description, IS_64BIT will be true
+   when called from amd64_linux_read_description, otherwise IS_64BIT will
+   be false.  If the *_linux_read_description functions found a cached
+   target description then this function will not be called.
+
+   Both GDB and gdbserver have their own implementations of this
+   function.  */
+
+extern void x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit);
+
+#endif /* ARCH_X86_LINUX_TDESC_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 4bcc0696027..24e1824b01c 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,8 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
+		arch/i386-linux-tdesc.o arch/x86-linux-tdesc-features.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -323,7 +324,8 @@ case ${gdb_host} in
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
 		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
-		nat/amd64-linux-siginfo.o"
+		nat/amd64-linux-siginfo.o arch/x86-linux-tdesc-features.o \
+		arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o"
 		;;
 	    sparc)
 		# Host: GNU/Linux UltraSPARC
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 47a674201f9..8326c458eb1 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -320,10 +320,13 @@ i[34567]86-*-linux*)
 	gdb_target_obs="i386-linux-tdep.o \
 			glibc-tdep.o \
 			solib-svr4.o symfile-mem.o \
-			linux-tdep.o linux-record.o"
+			linux-tdep.o linux-record.o \
+			arch/i386-linux-tdesc.o \
+			arch/x86-linux-tdesc-features.o"
 	if test "x$enable_64_bit_bfd" = "xyes"; then
 	    # Target: GNU/Linux x86-64
-	    gdb_target_obs="amd64-linux-tdep.o ${gdb_target_obs}"
+	    gdb_target_obs="amd64-linux-tdep.o \
+			    arch/amd64-linux-tdesc.o ${gdb_target_obs}"
 	fi
 	;;
 i[34567]86-*-gnu*)
@@ -718,7 +721,9 @@ x86_64-*-linux*)
 	# Target: GNU/Linux x86-64
 	gdb_target_obs="amd64-linux-tdep.o ${i386_tobjs}  \
 			i386-linux-tdep.o glibc-tdep.o \
-			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
+			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o \
+			arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o \
+			arch/x86-linux-tdesc-features.o"
 	;;
 x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
 	# Target: FreeBSD/amd64
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index c796f87780b..d4d820b9f98 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -40,6 +40,7 @@
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
@@ -679,29 +680,12 @@ i386_linux_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
 					  layout) != 0;
 }
 
-/* See i386-linux-tdep.h.  */
+/* See arch/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  if (xcr0 == 0)
-    return NULL;
-
-  static struct target_desc *i386_linux_tdescs \
-    [2/*X87*/][2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  struct target_desc **tdesc;
-
-  tdesc = &i386_linux_tdescs[(xcr0 & X86_XSTATE_X87) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
-
-  if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true, false);
-
-  return *tdesc;
+  /* Nothing.  */
 }
 
 /* Get Linux/x86 target description from core dump.  */
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7a2702d78bf..e17b5cf280c 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -110,12 +110,16 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} arch/x86-linux-tdesc-features.o"
+			srv_tgtobj="${srv_tgtobj} arch/i386-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			srv_linux_btrace=yes
 			ipa_obj="linux-i386-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/i386-ipa.o"
+			ipa_obj="${ipa_obj} arch/x86-linux-tdesc-features-ipa.o"
+			ipa_obj="${ipa_obj} arch/i386-linux-tdesc-ipa.o"
 			;;
   i[34567]86-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
@@ -374,12 +378,17 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
+			srv_tgtobj="${srv_tgtobj} arch/x86-linux-tdesc-features.o"
+			srv_tgtobj="${srv_tgtobj} arch/amd64-linux-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} arch/i386-linux-tdesc.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			srv_linux_btrace=yes
 			ipa_obj="linux-amd64-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/amd64-ipa.o"
+			ipa_obj="${ipa_obj} arch/x86-linux-tdesc-features-ipa.o"
+			ipa_obj="${ipa_obj} arch/amd64-linux-tdesc-ipa.o"
 			;;
   x86_64-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o"
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 0c80812cc6f..89857f208b8 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -20,9 +20,9 @@
 
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/amd64-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 /* fast tracepoints collect registers.  */
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index c1c3152fb04..8100c9f9840 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -20,9 +20,9 @@
 
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 4f473730efb..63607cae390 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -33,6 +33,7 @@
 #endif
 
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 #include "gdb_proc_service.h"
 /* Don't include elf/common.h if linux/elf.h got included by
@@ -48,7 +49,6 @@
 #include "nat/linux-nat.h"
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 5e12526bf17..13c80762605 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -17,295 +17,19 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+#include "arch/x86-linux-tdesc.h"
 #include "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#include "arch/amd64-linux-tdesc.h"
-#endif
 #include "x86-tdesc.h"
-#include "arch/i386-linux-tdesc.h"
-
-/* A structure used to describe a single cpu feature that might, or might
-   not, be checked for when creating a target description for one of i386,
-   amd64, or x32.  */
-
-struct x86_tdesc_feature {
-  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
-  uint64_t feature;
-
-  /* Is this feature checked when creating an i386 target description.  */
-  bool is_i386;
-
-  /* Is this feature checked when creating an amd64 target description.  */
-  bool is_amd64;
-
-  /* Is this feature checked when creating an x32 target description.  */
-  bool is_x32;
-};
-
-/* A constant table that describes all of the cpu features that are
-   checked when building a target description for i386, amd64, or x32.  */
-
-static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
-  /* Feature,           i386,	amd64,	x32.  */
-  { X86_XSTATE_PKRU,	true,	true, 	true },
-  { X86_XSTATE_AVX512,	true,	true, 	true },
-  { X86_XSTATE_AVX,	true,	true, 	true },
-  { X86_XSTATE_MPX,	true,	true, 	false },
-  { X86_XSTATE_SSE,	true,	false, 	false },
-  { X86_XSTATE_X87,	true,	false, 	false }
-};
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an i386 target description.  */
-
-static constexpr uint64_t
-x86_linux_i386_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an amd64 target description.  */
-
-static constexpr uint64_t
-x86_linux_amd64_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      mask |= entry.feature;
-
-  return mask;
-}
 
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an x32 target description.  */
+/* See arch/x86-linux-tdesc.h.  */
 
-static constexpr uint64_t
-x86_linux_x32_tdesc_feature_mask ()
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an i386 target description.  */
-
-static constexpr int
-x86_linux_i386_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an amd64 target description.  */
-
-static constexpr int
-x86_linux_amd64_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an x32 target description.  */
-
-static constexpr int
-x86_linux_x32_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_amd64_tdesc_count ()
-{
-  return x86_linux_amd64_tdesc_count_1 ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_x32_tdesc_count ()
-{
-  return x86_linux_x32_tdesc_count_1 ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_i386_tdesc_count ()
-{
-  return x86_linux_i386_tdesc_count_1 ();
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
-{
-  /* The following table shows which features are checked for when creating
-     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
-     represents the bit order within the generated index number.
-
-     i386  | x87 sse mpx avx avx512 pkru
-     amd64 |         mpx avx avx512 pkru
-     i32   |             avx avx512 pkru
-
-     The features are ordered so that for each mode (i386, amd64, i32) the
-     generated index will form a continuous range.  */
-
-  int idx = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-     if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
-	  == x86_linux_all_tdesc_features[i].feature)
-	idx |= (1 << i);
-    }
-
-  return idx;
-}
-
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
-{
-  uint64_t xcr0 = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_tdesc_features[i].feature;
-    }
-
-  return xcr0;
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-/* A cache of all possible i386 target descriptions.  */
-
-static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count_1 ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
-{
-  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count_1 ());
-
-  target_desc **tdesc = &i386_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = i386_create_target_description (xcr0, true, false);
-
-      init_target_desc (*tdesc, i386_expedite_regs);
-    }
-
-  return *tdesc;
-}
-#endif
-
 #ifdef __x86_64__
-
-/* A cache of all possible amd64 target descriptions.  */
-
-static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count_1 ()] = { };
-
-/* A cache of all possible x32 target descriptions.  */
-
-static target_desc *x32_tdescs[x86_linux_x32_tdesc_count_1 ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
-  if (is_x32)
-    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  if (is_64bit)
+    init_target_desc (tdesc, amd64_expedite_regs);
   else
-    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
-
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  if (is_x32)
-    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count_1 ());
-  else
-    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count_1 ());
-
-  target_desc **tdesc = nullptr;
-
-  if (is_x32)
-    tdesc = &x32_tdescs[idx];
-  else
-    tdesc = &amd64_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
-      init_target_desc (*tdesc, amd64_expedite_regs);
-    }
-  return *tdesc;
-}
-
 #endif
+    init_target_desc (tdesc, i386_expedite_regs);
+}
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 00/10] x86/Linux Target Description Changes
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (9 preceding siblings ...)
  2024-04-05 12:33       ` [PATCHv4 10/10] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
@ 2024-04-09 18:37       ` John Baldwin
  2024-04-25 13:35       ` Willgerodt, Felix
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
  12 siblings, 0 replies; 65+ messages in thread
From: John Baldwin @ 2024-04-09 18:37 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 4/5/24 8:33 AM, Andrew Burgess wrote:
> In v4:
> 
>    - I tried merging V3, but it turned out I broke pretty much
>      everything that wasn't x86 based when configured with
>      --enable-targets=all,
> 
>    - The problem was a failure to correctly split the shared code
>      between the gdb/arch/ and gdb/nat/ directories, as a consequence,
>      code which is needed on a non x86 based host to support x86 based
>      targets wasn't available to the compilation, and the build failed,
> 
>    - In V4 I've gone through every patch and resplit the code in a way
>      which I now believe is correct, I've done the following tests:
> 
>      + On a non x86 host I've built GDB to support only the current
>      host as a target, to support all targets, and to support x86-64
>      and i386 linux targets,
> 
>      + On an i386 virtual machine I built GDB only for the host as a
>      target, and for all targets.  I regression tested the all targets
>      build for unix, native-gdbserver, and native-extended-gdbserver,
> 
>      + On an x86-64 machine I've built GDB for only the current host as
>      a target, and for all targets.  I regression tested the all targets
>      build for unix, native-gdbserver, and native-extended-gdbserver.
> 
>    - Only patches 6, 8, and 10 require significant review.  All of the
>      other patches are pretty trivial (though reviews always welcome).
> 
>    - I think there's more improvements that can be made to the x86
>      target description creation/lookup/caching.  This series only
>      changes the Linux lookup, and we still cache i386/amd64/x32
>      separately.
> 
>      In the future I think we can merge all x86 target description
>      caching into a single data structure, this would be for all OS
>      variants and all ABI variants.
> 
>      Though making that "grand unification" will certainly require some
>      of the code in this series to change, I think the bulk of it will
>      remain, and trying to do everything in one series is just going to
>      result in an even larger series.  I'd prefer to get these first
>      patches merged, then come back to build on this work once this is
>      merged and we know there's no problems with it.
> 
> In v3:
> 
>    - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>      I've resolved, but am unable to test.  Reposting so the author of
>      that other commit can validate.
> 
>    - Initial testing looks good.  Full tests are still running.
> 
> In v2:
> 
>    - Rebase to current upstream/master, no merge conflicts,
> 
>    - Retested.

I re-read this series and it does look ok to me still, but I thought V3 was
ok as well. :-P  Your testing matrix described above for V4 seems reasonable
for catching what you ran into with V3.

Approved-By: John Baldwin <jhb@FreeBSD.org>

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 65+ messages in thread

* RE: [PATCHv4 00/10] x86/Linux Target Description Changes
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (10 preceding siblings ...)
  2024-04-09 18:37       ` [PATCHv4 00/10] x86/Linux Target Description Changes John Baldwin
@ 2024-04-25 13:35       ` Willgerodt, Felix
  2024-04-25 16:06         ` Andrew Burgess
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
  12 siblings, 1 reply; 65+ messages in thread
From: Willgerodt, Felix @ 2024-04-25 13:35 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Freitag, 5. April 2024 14:33
> To: gdb-patches@sourceware.org
> Cc: Andrew Burgess <aburgess@redhat.com>
> Subject: [PATCHv4 00/10] x86/Linux Target Description Changes
> 
> In v4:
> 
>   - I tried merging V3, but it turned out I broke pretty much
>     everything that wasn't x86 based when configured with
>     --enable-targets=all,
> 
>   - The problem was a failure to correctly split the shared code
>     between the gdb/arch/ and gdb/nat/ directories, as a consequence,
>     code which is needed on a non x86 based host to support x86 based
>     targets wasn't available to the compilation, and the build failed,
> 
>   - In V4 I've gone through every patch and resplit the code in a way
>     which I now believe is correct, I've done the following tests:
> 
>     + On a non x86 host I've built GDB to support only the current
>     host as a target, to support all targets, and to support x86-64
>     and i386 linux targets,
> 
>     + On an i386 virtual machine I built GDB only for the host as a
>     target, and for all targets.  I regression tested the all targets
>     build for unix, native-gdbserver, and native-extended-gdbserver,
> 
>     + On an x86-64 machine I've built GDB for only the current host as
>     a target, and for all targets.  I regression tested the all targets
>     build for unix, native-gdbserver, and native-extended-gdbserver.
> 
>   - Only patches 6, 8, and 10 require significant review.  All of the
>     other patches are pretty trivial (though reviews always welcome).
> 
>   - I think there's more improvements that can be made to the x86
>     target description creation/lookup/caching.  This series only
>     changes the Linux lookup, and we still cache i386/amd64/x32
>     separately.
> 
>     In the future I think we can merge all x86 target description
>     caching into a single data structure, this would be for all OS
>     variants and all ABI variants.
> 
>     Though making that "grand unification" will certainly require some
>     of the code in this series to change, I think the bulk of it will
>     remain, and trying to do everything in one series is just going to
>     result in an even larger series.  I'd prefer to get these first
>     patches merged, then come back to build on this work once this is
>     merged and we know there's no problems with it.
> 
> In v3:
> 
>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>     I've resolved, but am unable to test.  Reposting so the author of
>     that other commit can validate.
> 
>   - Initial testing looks good.  Full tests are still running.
> 
> In v2:
> 
>   - Rebase to current upstream/master, no merge conflicts,
> 
>   - Retested.
> 
> ---
> 
> Andrew Burgess (10):
>   gdbserver/ipa/x86: remove unneeded declarations
>   gdbserver: convert have_ptrace_getregset to a tribool
>   gdb/x86: move reading of cs and ds state into gdb/nat directory
>   gdbserver/x86: move no-xml code earlier in x86_linux_read_description
>   gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
>   gdb/gdbserver: share some code relating to target description creation
>   gdb/arch: assert that X86_XSTATE_MPX is not set for x32
>   gdbserver: update target description creation for x86/linux
>   gdb: move xcr0 == 0 check into i386_linux_core_read_description
>   gdb/gdbserver: share x86/linux tdesc caching
> 
>  gdb/Makefile.in                     |   8 +
>  gdb/amd64-linux-tdep.c              |  32 +---
>  gdb/amd64-linux-tdep.h              |   6 -
>  gdb/arch/amd64-linux-tdesc.c        |  61 +++++++
>  gdb/arch/amd64-linux-tdesc.h        |  30 ++++
>  gdb/arch/amd64.c                    |   8 +-
>  gdb/arch/i386-linux-tdesc.c         |  51 ++++++
>  gdb/arch/i386-linux-tdesc.h         |  29 ++++
>  gdb/arch/x86-linux-tdesc-features.c | 247 ++++++++++++++++++++++++++++
>  gdb/arch/x86-linux-tdesc-features.h |  62 +++++++
>  gdb/arch/x86-linux-tdesc.h          |  37 +++++
>  gdb/configure.nat                   |   8 +-
>  gdb/configure.tgt                   |  11 +-
>  gdb/i386-linux-tdep.c               |  42 ++---
>  gdb/i386-linux-tdep.h               |  23 ---
>  gdb/nat/x86-linux-tdesc.c           | 120 ++++++++++++++
>  gdb/nat/x86-linux-tdesc.h           |  60 +++++++
>  gdb/nat/x86-linux.c                 |  47 ++++++
>  gdb/nat/x86-linux.h                 |  28 ++++
>  gdb/x86-linux-nat.c                 | 123 ++------------
>  gdbserver/configure.srv             |  11 ++
>  gdbserver/linux-amd64-ipa.cc        |  50 ++----
>  gdbserver/linux-arm-low.cc          |   6 +-
>  gdbserver/linux-i386-ipa.cc         |  26 +--
>  gdbserver/linux-low.cc              |   2 +-
>  gdbserver/linux-low.h               |   2 +-
>  gdbserver/linux-x86-low.cc          | 192 +++++++--------------
>  gdbserver/linux-x86-tdesc.cc        | 142 +---------------
>  gdbserver/linux-x86-tdesc.h         |  56 -------
>  gdbsupport/x86-xstate.h             |  20 +++
>  30 files changed, 949 insertions(+), 591 deletions(-)
>  create mode 100644 gdb/arch/amd64-linux-tdesc.c
>  create mode 100644 gdb/arch/amd64-linux-tdesc.h
>  create mode 100644 gdb/arch/i386-linux-tdesc.c
>  create mode 100644 gdb/arch/i386-linux-tdesc.h
>  create mode 100644 gdb/arch/x86-linux-tdesc-features.c
>  create mode 100644 gdb/arch/x86-linux-tdesc-features.h
>  create mode 100644 gdb/arch/x86-linux-tdesc.h
>  create mode 100644 gdb/nat/x86-linux-tdesc.c
>  create mode 100644 gdb/nat/x86-linux-tdesc.h
>  delete mode 100644 gdbserver/linux-x86-tdesc.h
> 
> 
> base-commit: 16810e455feb26ef826a3ed876d6d7e6d24818b0
> --
> 2.25.4


Hi Andrew,

Thanks for doing this, I like what this does.
I am still in the middle of reviewing this. Though in first testing I saw
that I can't build a 32-bit gdbserver on Ubuntu 22.04.
I am building like this: 

export CFLAGS="-g -O0 -m32"
export CXXFLAGS="${CFLAGS} -m32"
export LDFLAGS="-m32"

$src/gdb/configure \                                                            
     --build="x86_64-linux-gnu" \                                                
     --host="i686-linux-gnu" \                                                   
     --disable-gdb

make -s -j$(nproc)  all-gdbserver

Not sure if there is an easier way of doing this, but this works for master.
With your series I see this:

  CXX    nat/x86-linux-tdesc.o
/gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c: In function ?const target_desc*
x86_linux_tdesc_for_tid(int, tribool*, gdb::function_view<void(long long unsigned int)>, const char*, uint64_t*)?:
/gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:54:7: error: ?have_ptrace_getfpxregs? was not declared in this scope;
did you mean ?have_ptrace_getregset??
   54 |   if (have_ptrace_getfpxregs == -1)
      |       ^~~~~~~~~~~~~~~~~~~~~~
      |       have_ptrace_getregset
/gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:56:7: error: ?elf_fpxregset_t? was not declared in this scope
   56 |       elf_fpxregset_t fpxregs;
      |       ^~~~~~~~~~~~~~~
In file included from /gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:29:
/gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:58:53: error: ?fpxregs? was not declared in this scope
   58 |       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
      |                                                     ^~~~~~~
/gdb/gdbserver/../gdb/nat/gdb_ptrace.h:142:57: note: in definition of macro ?ptrace?
  142 |          ptrace ((PTRACE_TYPE_ARG1) request, pid, addr, data)
      |                                                         ^~~~
make[1]: *** [Makefile:577: nat/x86-linux-tdesc.o] Error 1
make: *** [Makefile:13074: install-gdbserver] Error 2

I didn't investigate further yet, sorry.

Regards,
Felix
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 65+ messages in thread

* RE: [PATCHv4 00/10] x86/Linux Target Description Changes
  2024-04-25 13:35       ` Willgerodt, Felix
@ 2024-04-25 16:06         ` Andrew Burgess
  0 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-25 16:06 UTC (permalink / raw)
  To: Willgerodt, Felix, gdb-patches

"Willgerodt, Felix" <felix.willgerodt@intel.com> writes:

>> -----Original Message-----
>> From: Andrew Burgess <aburgess@redhat.com>
>> Sent: Freitag, 5. April 2024 14:33
>> To: gdb-patches@sourceware.org
>> Cc: Andrew Burgess <aburgess@redhat.com>
>> Subject: [PATCHv4 00/10] x86/Linux Target Description Changes
>> 
>> In v4:
>> 
>>   - I tried merging V3, but it turned out I broke pretty much
>>     everything that wasn't x86 based when configured with
>>     --enable-targets=all,
>> 
>>   - The problem was a failure to correctly split the shared code
>>     between the gdb/arch/ and gdb/nat/ directories, as a consequence,
>>     code which is needed on a non x86 based host to support x86 based
>>     targets wasn't available to the compilation, and the build failed,
>> 
>>   - In V4 I've gone through every patch and resplit the code in a way
>>     which I now believe is correct, I've done the following tests:
>> 
>>     + On a non x86 host I've built GDB to support only the current
>>     host as a target, to support all targets, and to support x86-64
>>     and i386 linux targets,
>> 
>>     + On an i386 virtual machine I built GDB only for the host as a
>>     target, and for all targets.  I regression tested the all targets
>>     build for unix, native-gdbserver, and native-extended-gdbserver,
>> 
>>     + On an x86-64 machine I've built GDB for only the current host as
>>     a target, and for all targets.  I regression tested the all targets
>>     build for unix, native-gdbserver, and native-extended-gdbserver.
>> 
>>   - Only patches 6, 8, and 10 require significant review.  All of the
>>     other patches are pretty trivial (though reviews always welcome).
>> 
>>   - I think there's more improvements that can be made to the x86
>>     target description creation/lookup/caching.  This series only
>>     changes the Linux lookup, and we still cache i386/amd64/x32
>>     separately.
>> 
>>     In the future I think we can merge all x86 target description
>>     caching into a single data structure, this would be for all OS
>>     variants and all ABI variants.
>> 
>>     Though making that "grand unification" will certainly require some
>>     of the code in this series to change, I think the bulk of it will
>>     remain, and trying to do everything in one series is just going to
>>     result in an even larger series.  I'd prefer to get these first
>>     patches merged, then come back to build on this work once this is
>>     merged and we know there's no problems with it.
>> 
>> In v3:
>> 
>>   - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
>>     I've resolved, but am unable to test.  Reposting so the author of
>>     that other commit can validate.
>> 
>>   - Initial testing looks good.  Full tests are still running.
>> 
>> In v2:
>> 
>>   - Rebase to current upstream/master, no merge conflicts,
>> 
>>   - Retested.
>> 
>> ---
>> 
>> Andrew Burgess (10):
>>   gdbserver/ipa/x86: remove unneeded declarations
>>   gdbserver: convert have_ptrace_getregset to a tribool
>>   gdb/x86: move reading of cs and ds state into gdb/nat directory
>>   gdbserver/x86: move no-xml code earlier in x86_linux_read_description
>>   gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
>>   gdb/gdbserver: share some code relating to target description creation
>>   gdb/arch: assert that X86_XSTATE_MPX is not set for x32
>>   gdbserver: update target description creation for x86/linux
>>   gdb: move xcr0 == 0 check into i386_linux_core_read_description
>>   gdb/gdbserver: share x86/linux tdesc caching
>> 
>>  gdb/Makefile.in                     |   8 +
>>  gdb/amd64-linux-tdep.c              |  32 +---
>>  gdb/amd64-linux-tdep.h              |   6 -
>>  gdb/arch/amd64-linux-tdesc.c        |  61 +++++++
>>  gdb/arch/amd64-linux-tdesc.h        |  30 ++++
>>  gdb/arch/amd64.c                    |   8 +-
>>  gdb/arch/i386-linux-tdesc.c         |  51 ++++++
>>  gdb/arch/i386-linux-tdesc.h         |  29 ++++
>>  gdb/arch/x86-linux-tdesc-features.c | 247 ++++++++++++++++++++++++++++
>>  gdb/arch/x86-linux-tdesc-features.h |  62 +++++++
>>  gdb/arch/x86-linux-tdesc.h          |  37 +++++
>>  gdb/configure.nat                   |   8 +-
>>  gdb/configure.tgt                   |  11 +-
>>  gdb/i386-linux-tdep.c               |  42 ++---
>>  gdb/i386-linux-tdep.h               |  23 ---
>>  gdb/nat/x86-linux-tdesc.c           | 120 ++++++++++++++
>>  gdb/nat/x86-linux-tdesc.h           |  60 +++++++
>>  gdb/nat/x86-linux.c                 |  47 ++++++
>>  gdb/nat/x86-linux.h                 |  28 ++++
>>  gdb/x86-linux-nat.c                 | 123 ++------------
>>  gdbserver/configure.srv             |  11 ++
>>  gdbserver/linux-amd64-ipa.cc        |  50 ++----
>>  gdbserver/linux-arm-low.cc          |   6 +-
>>  gdbserver/linux-i386-ipa.cc         |  26 +--
>>  gdbserver/linux-low.cc              |   2 +-
>>  gdbserver/linux-low.h               |   2 +-
>>  gdbserver/linux-x86-low.cc          | 192 +++++++--------------
>>  gdbserver/linux-x86-tdesc.cc        | 142 +---------------
>>  gdbserver/linux-x86-tdesc.h         |  56 -------
>>  gdbsupport/x86-xstate.h             |  20 +++
>>  30 files changed, 949 insertions(+), 591 deletions(-)
>>  create mode 100644 gdb/arch/amd64-linux-tdesc.c
>>  create mode 100644 gdb/arch/amd64-linux-tdesc.h
>>  create mode 100644 gdb/arch/i386-linux-tdesc.c
>>  create mode 100644 gdb/arch/i386-linux-tdesc.h
>>  create mode 100644 gdb/arch/x86-linux-tdesc-features.c
>>  create mode 100644 gdb/arch/x86-linux-tdesc-features.h
>>  create mode 100644 gdb/arch/x86-linux-tdesc.h
>>  create mode 100644 gdb/nat/x86-linux-tdesc.c
>>  create mode 100644 gdb/nat/x86-linux-tdesc.h
>>  delete mode 100644 gdbserver/linux-x86-tdesc.h
>> 
>> 
>> base-commit: 16810e455feb26ef826a3ed876d6d7e6d24818b0
>> --
>> 2.25.4
>
>
> Hi Andrew,
>
> Thanks for doing this, I like what this does.
> I am still in the middle of reviewing this. Though in first testing I saw
> that I can't build a 32-bit gdbserver on Ubuntu 22.04.
> I am building like this: 
>
> export CFLAGS="-g -O0 -m32"
> export CXXFLAGS="${CFLAGS} -m32"
> export LDFLAGS="-m32"
>
> $src/gdb/configure \                                                            
>      --build="x86_64-linux-gnu" \                                                
>      --host="i686-linux-gnu" \                                                   
>      --disable-gdb
>
> make -s -j$(nproc)  all-gdbserver
>
> Not sure if there is an easier way of doing this, but this works for master.
> With your series I see this:
>
>   CXX    nat/x86-linux-tdesc.o
> /gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c: In function ?const target_desc*
> x86_linux_tdesc_for_tid(int, tribool*, gdb::function_view<void(long long unsigned int)>, const char*, uint64_t*)?:
> /gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:54:7: error: ?have_ptrace_getfpxregs? was not declared in this scope;
> did you mean ?have_ptrace_getregset??
>    54 |   if (have_ptrace_getfpxregs == -1)
>       |       ^~~~~~~~~~~~~~~~~~~~~~
>       |       have_ptrace_getregset
> /gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:56:7: error: ?elf_fpxregset_t? was not declared in this scope
>    56 |       elf_fpxregset_t fpxregs;
>       |       ^~~~~~~~~~~~~~~
> In file included from /gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:29:
> /gdb/gdbserver/../gdb/nat/x86-linux-tdesc.c:58:53: error: ?fpxregs? was not declared in this scope
>    58 |       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
>       |                                                     ^~~~~~~
> /gdb/gdbserver/../gdb/nat/gdb_ptrace.h:142:57: note: in definition of macro ?ptrace?
>   142 |          ptrace ((PTRACE_TYPE_ARG1) request, pid, addr, data)
>       |                                                         ^~~~
> make[1]: *** [Makefile:577: nat/x86-linux-tdesc.o] Error 1
> make: *** [Makefile:13074: install-gdbserver] Error 2
>
> I didn't investigate further yet, sorry.

Thanks for bringing this to my attention.  I'll investigate this
tomorrow.

Thanks,
Andrew


>
> Regards,
> Felix
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 00/11] x86/Linux Target Description Changes
  2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
                         ` (11 preceding siblings ...)
  2024-04-25 13:35       ` Willgerodt, Felix
@ 2024-04-26 15:01       ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 01/11] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
                           ` (10 more replies)
  12 siblings, 11 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

In v5:

  - Felix pointed out that building gdbserver with the '-m32' flag on
    an x86-64 host would fail.  This is fixed in V5 with the addition
    of patch #4.  This patch moves the have_ptrace_getfpxregs global
    into the gdb/nat/ directory and fixes the includes so that the
    the declaration is seen where needed,

  - I've rebased onto a slightly later commit.

In v4:

  - I tried merging V3, but it turned out I broke pretty much
    everything that wasn't x86 based when configured with
    --enable-targets=all,

  - The problem was a failure to correctly split the shared code
    between the gdb/arch/ and gdb/nat/ directories, as a consequence,
    code which is needed on a non x86 based host to support x86 based
    targets wasn't available to the compilation, and the build failed,

  - In V4 I've gone through every patch and resplit the code in a way
    which I now believe is correct, I've done the following tests:

    + On a non x86 host I've built GDB to support only the current
    host as a target, to support all targets, and to support x86-64
    and i386 linux targets,

    + On an i386 virtual machine I built GDB only for the host as a
    target, and for all targets.  I regression tested the all targets
    build for unix, native-gdbserver, and native-extended-gdbserver,

    + On an x86-64 machine I've built GDB for only the current host as
    a target, and for all targets.  I regression tested the all targets
    build for unix, native-gdbserver, and native-extended-gdbserver.

  - Only patches 6, 8, and 10 require significant review.  All of the
    other patches are pretty trivial (though reviews always welcome).

  - I think there's more improvements that can be made to the x86
    target description creation/lookup/caching.  This series only
    changes the Linux lookup, and we still cache i386/amd64/x32
    separately.

    In the future I think we can merge all x86 target description
    caching into a single data structure, this would be for all OS
    variants and all ABI variants.

    Though making that "grand unification" will certainly require some
    of the code in this series to change, I think the bulk of it will
    remain, and trying to do everything in one series is just going to
    result in an even larger series.  I'd prefer to get these first
    patches merged, then come back to build on this work once this is
    merged and we know there's no problems with it.

In v3:

  - Rebased.  Nasty merge conflict with 4bb20a6244b7091 which I think
    I've resolved, but am unable to test.  Reposting so the author of
    that other commit can validate.

  - Initial testing looks good.  Full tests are still running.

In v2:

  - Rebase to current upstream/master, no merge conflicts,

  - Retested.

---

Andrew Burgess (11):
  gdbserver/ipa/x86: remove unneeded declarations
  gdbserver: convert have_ptrace_getregset to a tribool
  gdb/x86: move reading of cs and ds state into gdb/nat directory
  gdb/x86: move have_ptrace_getfpxregs global into gdb/nat directory
  gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  gdb/gdbserver: share some code relating to target description creation
  gdb/arch: assert that X86_XSTATE_MPX is not set for x32
  gdbserver: update target description creation for x86/linux
  gdb: move xcr0 == 0 check into i386_linux_core_read_description
  gdb/gdbserver: share x86/linux tdesc caching

 gdb/Makefile.in                               |   9 +-
 gdb/amd64-linux-tdep.c                        |  32 +--
 gdb/amd64-linux-tdep.h                        |   6 -
 gdb/arch/amd64-linux-tdesc.c                  |  61 +++++
 gdb/arch/amd64-linux-tdesc.h                  |  30 +++
 gdb/arch/amd64.c                              |   8 +-
 gdb/arch/i386-linux-tdesc.c                   |  51 ++++
 .../i386-linux-tdesc.h}                       |  17 +-
 gdb/arch/x86-linux-tdesc-features.c           | 247 ++++++++++++++++++
 gdb/arch/x86-linux-tdesc-features.h           |  62 +++++
 gdb/arch/x86-linux-tdesc.h                    |  37 +++
 gdb/configure.nat                             |   8 +-
 gdb/configure.tgt                             |  11 +-
 gdb/i386-linux-nat.c                          |  18 +-
 gdb/i386-linux-tdep.c                         |  42 +--
 gdb/i386-linux-tdep.h                         |  23 --
 gdb/nat/x86-linux-tdesc.c                     | 124 +++++++++
 gdb/nat/x86-linux-tdesc.h                     |  60 +++++
 gdb/nat/x86-linux.c                           |  56 ++++
 gdb/nat/x86-linux.h                           |  44 ++++
 gdb/x86-linux-nat.c                           | 126 +--------
 gdbserver/configure.srv                       |  11 +
 gdbserver/linux-amd64-ipa.cc                  |  50 +---
 gdbserver/linux-arm-low.cc                    |   6 +-
 gdbserver/linux-i386-ipa.cc                   |  26 +-
 gdbserver/linux-low.cc                        |   2 +-
 gdbserver/linux-low.h                         |   2 +-
 gdbserver/linux-x86-low.cc                    | 201 +++++---------
 gdbserver/linux-x86-tdesc.cc                  | 142 +---------
 gdbserver/linux-x86-tdesc.h                   |  56 ----
 gdbsupport/x86-xstate.h                       |  20 ++
 31 files changed, 959 insertions(+), 629 deletions(-)
 create mode 100644 gdb/arch/amd64-linux-tdesc.c
 create mode 100644 gdb/arch/amd64-linux-tdesc.h
 create mode 100644 gdb/arch/i386-linux-tdesc.c
 rename gdb/{i386-linux-nat.h => arch/i386-linux-tdesc.h} (64%)
 create mode 100644 gdb/arch/x86-linux-tdesc-features.c
 create mode 100644 gdb/arch/x86-linux-tdesc-features.h
 create mode 100644 gdb/arch/x86-linux-tdesc.h
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h
 delete mode 100644 gdbserver/linux-x86-tdesc.h


base-commit: 39f0ac383169d7ef8b3169e483ae95dd185e41bd
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 01/11] gdbserver/ipa/x86: remove unneeded declarations
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 02/11] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
                           ` (9 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

Spotted some declarations in gdbserver/linux-amd64-ipa.cc that are no
longer needed.  These are:

  1. 'init_registers_amd64_linux' - the comment claims this function
  is auto generated, but I don't believe that this is still the case.
  Also the function is not used in this file,

  2. 'tdesc_amd64_linux' - this variable doesn't seem to exist any
  more, I suspect this was renamed to 'tdesc_amd64_linux_no_xml', but
  neither are used in this file, so lets remove the declaration.

The amd64 in-process-agent still builds fine after this commit.

There should be no user visible changes after this commit.
---
 gdbserver/linux-amd64-ipa.cc | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 13c8a5bb360..a6346750f49 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -23,10 +23,6 @@
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 
-/* Defined in auto-generated file amd64-linux.c.  */
-void init_registers_amd64_linux (void);
-extern const struct target_desc *tdesc_amd64_linux;
-
 /* fast tracepoints collect registers.  */
 
 #define FT_CR_RIP 0
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 02/11] gdbserver: convert have_ptrace_getregset to a tribool
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 01/11] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 03/11] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
                           ` (8 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

Convert the have_ptrace_getregset global within gdbserver to a
tribool.  This brings the flag into alignment with the corresponding
flag in GDB.

The gdbserver have_ptrace_getregset variable is already used as a
tribool, it just doesn't have the tribool type.

In a future commit I plan to share more code between GDB and
gdbserver, and having this variable be the same type in both code
bases will make the sharing much easier.

There should be no user visible changes after this commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-arm-low.cc |  6 +++---
 gdbserver/linux-low.cc     |  2 +-
 gdbserver/linux-low.h      |  2 +-
 gdbserver/linux-x86-low.cc | 10 +++++-----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 17b64c09aff..eec4649b235 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -1006,9 +1006,9 @@ arm_target::low_arch_setup ()
 
   /* Check if PTRACE_GETREGSET works.  */
   if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
-    have_ptrace_getregset = 1;
+    have_ptrace_getregset = TRIBOOL_TRUE;
   else
-    have_ptrace_getregset = 0;
+    have_ptrace_getregset = TRIBOOL_FALSE;
 }
 
 bool
@@ -1121,7 +1121,7 @@ arm_target::get_regs_info ()
 {
   const struct target_desc *tdesc = current_process ()->tdesc;
 
-  if (have_ptrace_getregset == 1
+  if (have_ptrace_getregset == TRIBOOL_TRUE
       && (is_aarch32_linux_description (tdesc)
 	  || arm_linux_get_tdesc_fp_type (tdesc) == ARM_FP_TYPE_VFPV3))
     return &regs_info_aarch32;
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 9614cd7150c..ac7f9807ecc 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -134,7 +134,7 @@ typedef struct
 #endif
 
 /* Does the current host support PTRACE_GETREGSET?  */
-int have_ptrace_getregset = -1;
+enum tribool have_ptrace_getregset = TRIBOOL_UNKNOWN;
 
 /* Return TRUE if THREAD is the leader thread of the process.  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index d34d2738238..eaf87527338 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -951,7 +951,7 @@ void thread_db_notice_clone (struct thread_info *parent_thr, ptid_t child_ptid);
 
 bool thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len);
 
-extern int have_ptrace_getregset;
+extern enum tribool have_ptrace_getregset;
 
 /* Search for the value with type MATCH in the auxv vector, with entries of
    length WORDSIZE bytes, of process with pid PID.  If found, store the
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 2532603451a..2603fb2ac5d 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -898,7 +898,7 @@ x86_linux_read_description (void)
       if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
 	{
 	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = 0;
+	  have_ptrace_getregset = TRIBOOL_FALSE;
 	  return i386_linux_read_description (X86_XSTATE_X87);
 	}
       else
@@ -917,7 +917,7 @@ x86_linux_read_description (void)
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-  if (have_ptrace_getregset == -1)
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
       struct iovec iov;
@@ -928,10 +928,10 @@ x86_linux_read_description (void)
       /* Check if PTRACE_GETREGSET works.  */
       if (ptrace (PTRACE_GETREGSET, tid,
 		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = 0;
+	have_ptrace_getregset = TRIBOOL_FALSE;
       else
 	{
-	  have_ptrace_getregset = 1;
+	  have_ptrace_getregset = TRIBOOL_TRUE;
 
 	  /* Get XCR0 from XSAVE extended state.  */
 	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
@@ -954,7 +954,7 @@ x86_linux_read_description (void)
     }
 
   /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset
+  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
 		   && (xcr0 & X86_XSTATE_ALL_MASK));
 
   if (xcr0_features)
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 03/11] gdb/x86: move reading of cs and ds state into gdb/nat directory
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 01/11] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 02/11] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 04/11] gdb/x86: move have_ptrace_getfpxregs global " Andrew Burgess
                           ` (7 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

This patch is part of a series that has the aim sharing the x86 Linux
target description creation code between GDB and gdbserver.

Within GDB part of this process involves reading the cs and ds state
from the 'struct user_regs_struct' using a ptrace call.

This isn't done by gdbserver, which is part of the motivation for this
whole series; the approach gdbserver takes is inferior to the approach
GDB takes (gdbserver relies on reading the file being debugged, and
extracting similar information from the file headers).

This commit moves the reading of cs and ds, which is used to figure
out if a thread is 32-bit or 64-bit (or in x32 mode), into the gdb/nat
directory so that the code can be shared with gdbserver, but at this
point I'm not actually using the code in gdbserver, that will come
later.

As such there should be no user visible changes after this commit, GDB
continues to do things as it did before (reading cs/ds), while
gdbserver continues to use its own approach (which doesn't require
reading cs/ds).

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/nat/x86-linux.c | 47 +++++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/x86-linux.h | 28 +++++++++++++++++++++++++++
 gdb/x86-linux-nat.c | 42 +++++-----------------------------------
 3 files changed, 80 insertions(+), 37 deletions(-)

diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index 7a21c8f2c26..ad3ed3c2289 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -19,6 +19,8 @@
 
 #include "x86-linux.h"
 #include "x86-linux-dregs.h"
+#include "nat/gdb_ptrace.h"
+#include <sys/user.h>
 
 /* Per-thread arch-specific data we want to keep.  */
 
@@ -79,3 +81,48 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp)
 {
   x86_linux_update_debug_registers (lwp);
 }
+
+#ifdef __x86_64__
+/* Value of CS segment register:
+     64bit process: 0x33
+     32bit process: 0x23  */
+#define AMD64_LINUX_USER64_CS 0x33
+
+/* Value of DS segment register:
+     LP64 process: 0x0
+     X32 process: 0x2b  */
+#define AMD64_LINUX_X32_DS 0x2b
+#endif
+
+/* See nat/x86-linux.h.  */
+
+x86_linux_arch_size
+x86_linux_ptrace_get_arch_size (int tid)
+{
+#ifdef __x86_64__
+  unsigned long cs;
+  unsigned long ds;
+
+  /* Get CS register.  */
+  errno = 0;
+  cs = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, cs), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get CS register"));
+
+  bool is_64bit = cs == AMD64_LINUX_USER64_CS;
+
+  /* Get DS register.  */
+  errno = 0;
+  ds = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, ds), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get DS register"));
+
+  bool is_x32 = ds == AMD64_LINUX_X32_DS;
+
+  return x86_linux_arch_size (is_64bit, is_x32);
+#else
+  return x86_linux_arch_size (false, false);
+#endif
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 822882173f9..15153ea277e 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -47,4 +47,32 @@ extern void x86_linux_delete_thread (struct arch_lwp_info *arch_lwp);
 
 extern void x86_linux_prepare_to_resume (struct lwp_info *lwp);
 
+/* Return value from x86_linux_ptrace_get_arch_size function.  Indicates if
+   a thread is 32-bit, 64-bit, or x32.  */
+
+struct x86_linux_arch_size
+{
+  explicit x86_linux_arch_size (bool is_64bit, bool is_x32)
+    : m_is_64bit (is_64bit),
+      m_is_x32 (is_x32)
+  {
+    /* Nothing.  */
+  }
+
+  bool is_64bit () const
+  { return m_is_64bit; }
+
+  bool is_x32 () const
+  { return m_is_x32; }
+
+private:
+  bool m_is_64bit = false;
+  bool m_is_x32 = false;
+};
+
+/* Use ptrace calls to figure out if thread TID is 32-bit, 64-bit, or
+   64-bit running in x32 mode.  */
+
+extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
+
 #endif /* NAT_X86_LINUX_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index a3d8ffb60f1..f91db492d05 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -90,18 +90,6 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
   linux_nat_target::post_startup_inferior (ptid);
 }
 
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
 /* Get Linux/x86 target description from running target.  */
 
 const struct target_desc *
@@ -121,31 +109,11 @@ x86_linux_nat_target::read_description ()
   tid = inferior_ptid.pid ();
 
 #ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-		 offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  is_64bit = arch_size.is_64bit ();
+  is_x32 = arch_size.is_x32 ();
+
 #elif HAVE_PTRACE_GETFPXREGS
   if (have_ptrace_getfpxregs == -1)
     {
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 04/11] gdb/x86: move have_ptrace_getfpxregs global into gdb/nat directory
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (2 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 03/11] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 05/11] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
                           ` (6 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

The have_ptrace_getfpxregs global tracks whether GDB or gdbserver is
running on a kernel that supports the GETFPXREGS ptrace request.

Currently this global is declared twice (once in GDB and once in
gdbserver), but it makes sense to move this global into the nat/
directory, and have a single declaration and definition.

This is a refactor, there should be no user visible changes after this
commit.
---
 gdb/Makefile.in            |  1 -
 gdb/i386-linux-nat.c       | 18 +-----------------
 gdb/i386-linux-nat.h       | 26 --------------------------
 gdb/nat/x86-linux.c        |  9 +++++++++
 gdb/nat/x86-linux.h        | 16 ++++++++++++++++
 gdb/x86-linux-nat.c        |  3 ---
 gdbserver/linux-x86-low.cc | 11 -----------
 7 files changed, 26 insertions(+), 58 deletions(-)
 delete mode 100644 gdb/i386-linux-nat.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 23894ea4a4d..353e45b3cec 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1396,7 +1396,6 @@ HFILES_NO_SRCDIR = \
 	hppa-tdep.h \
 	i386-bsd-nat.h \
 	i386-darwin-tdep.h \
-	i386-linux-nat.h \
 	i386-linux-tdep.h \
 	i386-tdep.h \
 	i387-tdep.h \
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 7278dd91ff0..32d9d998beb 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -26,7 +26,7 @@
 #include "gregset.h"
 #include "gdb_proc_service.h"
 
-#include "i386-linux-nat.h"
+#include "nat/x86-linux.h"
 #include "i387-tdep.h"
 #include "i386-tdep.h"
 #include "i386-linux-tdep.h"
@@ -80,22 +80,6 @@ int have_ptrace_getregs =
   0
 #endif
 ;
-
-/* Does the current host support the GETFPXREGS request?  The header
-   file may or may not define it, and even if it is defined, the
-   kernel will return EIO if it's running on a pre-SSE processor.
-
-   My instinct is to attach this to some architecture- or
-   target-specific data structure, but really, a particular GDB
-   process can only run on top of one kernel at a time.  So it's okay
-   for this to be a simple variable.  */
-int have_ptrace_getfpxregs =
-#ifdef HAVE_PTRACE_GETFPXREGS
-  -1
-#else
-  0
-#endif
-;
 \f
 
 /* Accessing registers through the U area, one at a time.  */
diff --git a/gdb/i386-linux-nat.h b/gdb/i386-linux-nat.h
deleted file mode 100644
index c3d32aff1be..00000000000
--- a/gdb/i386-linux-nat.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Native-dependent code for GNU/Linux i386.
-
-   Copyright (C) 1999-2024 Free Software Foundation, Inc.
-
-   This file is part of GDB.
-
-   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 <http://www.gnu.org/licenses/>.  */
-
-#ifndef I386_LINUX_NAT_H
-#define I386_LINUX_NAT_H 1
-
-/* Does the current host support the GETFPXREGS request? */
-extern int have_ptrace_getfpxregs;
-
-#endif
diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index ad3ed3c2289..35a3e960946 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -22,6 +22,15 @@
 #include "nat/gdb_ptrace.h"
 #include <sys/user.h>
 
+/* See nat/x86-linux.h.  */
+int have_ptrace_getfpxregs =
+#ifdef HAVE_PTRACE_GETFPXREGS
+  -1
+#else
+  0
+#endif
+;
+
 /* Per-thread arch-specific data we want to keep.  */
 
 struct arch_lwp_info
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index 15153ea277e..061b3c2e394 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -22,6 +22,22 @@
 
 #include "nat/linux-nat.h"
 
+/* Does the current host support the GETFPXREGS request?  The system header
+   file may or may not define it, but even if it is defined, the kernel
+   will return EIO if it's running on a pre-SSE processor.
+
+   A value of -1 indicates that we don't know yet, we've not yet tried the
+   ptrace call; a value of 0 indicates we've previously tried the ptrace
+   call and it failed, indicating this request is not supported; and a
+   value of 1 indicates that we've previously tried the ptrace call and it
+   has been successful.
+
+   My instinct is to attach this to some architecture- or target-specific
+   data structure, but really, a particular GDB process can only run on top
+   of one kernel at a time.  So it's okay - for this to be a global
+   variable.  */
+extern int have_ptrace_getfpxregs;
+
 /* Set whether our local mirror of LWP's debug registers has been
    changed since the values were last written to the thread.  Nonzero
    indicates that a change has been made, zero indicates no change.  */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index f91db492d05..12965c7a21b 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -26,9 +26,6 @@
 #include <sys/uio.h>
 
 #include "x86-nat.h"
-#ifndef __x86_64__
-#include "i386-linux-nat.h"
-#endif
 #include "x86-linux-nat.h"
 #include "i386-linux-tdep.h"
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 2603fb2ac5d..8199b0ccc09 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -852,17 +852,6 @@ static int use_xml;
   the process/thread is in.  */
 #define I386_LINUX_XSAVE_XCR0_OFFSET 464
 
-/* Does the current host support the GETFPXREGS request?  The header
-   file may or may not define it, and even if it is defined, the
-   kernel will return EIO if it's running on a pre-SSE processor.  */
-int have_ptrace_getfpxregs =
-#ifdef HAVE_PTRACE_GETFPXREGS
-  -1
-#else
-  0
-#endif
-;
-
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 05/11] gdbserver/x86: move no-xml code earlier in x86_linux_read_description
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (3 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 04/11] gdb/x86: move have_ptrace_getfpxregs global " Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 06/11] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
                           ` (5 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

This commit is part of a series that aims to share more of the x86
target description reading/generation code between GDB and gdbserver.

There are a huge number of similarities between the code in
gdbserver's x86_linux_read_description function and GDB's
x86_linux_nat_target::read_description function, and it is this
similarity that I plan, in a later commit, to share between GDB and
gdbserver.

However, one thing that is different in x86_linux_read_description is
the code inside the '!use_xml' block.  This is the code that handles
the case where gdbserver is not allowed to send an XML target
description back to GDB.  In this case gdbserver uses some predefined,
fixed, target descriptions.

First, it's worth noting that I suspect this code is not tested any
more.  I couldn't find anything in the testsuite that tries to disable
XML target description support.  And the idea of having a single
"fixed" target description really doesn't work well when we think
about all the various x86 extensions that exist.  Part of me would
like to rip out the no-xml support in gdbserver (at least for x86),
and if a GDB connects that doesn't support XML target descriptions,
gdbserver can just give an error and drop the connection.  GDB has
supported XML target descriptions for 16 years now, I think it would
be reasonable for our shipped gdbserver to drop support for the old
way of doing things.

Anyway.... this commit doesn't do that.

What I did notice was that, over time, the '!use_xml' block appears to
have "drifted" within the x86_linux_read_description function; it's
now not the first check we do.  Instead we make some ptrace calls and
return a target description generated based on the result of these
ptrace calls.  Surely it only makes sense to generate variable target
descriptions if we can send these back to GDB?

So in this commit I propose to move the '!use_xml' block earlier in
the x86_linux_read_description function.

The benefit of this is that this leaves the later half of
x86_linux_read_description much more similar to the GDB function
x86_linux_nat_target::read_description and sets us up for potentially
sharing code between GDB and gdbserver in a later commit.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-x86-low.cc | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 8199b0ccc09..ffb2a90731d 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -879,6 +879,22 @@ x86_linux_read_description (void)
 #endif
     }
 
+  /* If we are not allowed to send an XML target description then we need
+     to use the hard-wired target descriptions.  This corresponds to GDB's
+     default machine for x86.
+
+     This check needs to occur before any returns statements that might
+     generate some alternative target descriptions.  */
+  if (!use_xml)
+    {
+#ifdef __x86_64__
+      if (machine == EM_X86_64)
+	return tdesc_amd64_linux_no_xml.get ();
+      else
+#endif
+	return tdesc_i386_linux_no_xml.get ();
+    }
+
 #if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
   if (machine == EM_386 && have_ptrace_getfpxregs == -1)
     {
@@ -895,17 +911,6 @@ x86_linux_read_description (void)
     }
 #endif
 
-  if (!use_xml)
-    {
-      /* Don't use XML.  */
-#ifdef __x86_64__
-      if (machine == EM_X86_64)
-	return tdesc_amd64_linux_no_xml.get ();
-      else
-#endif
-	return tdesc_i386_linux_no_xml.get ();
-    }
-
   if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
     {
       uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 06/11] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (4 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 05/11] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 07/11] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
                           ` (4 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

Share the definition of I386_LINUX_XSAVE_XCR0_OFFSET between GDB and
gdbserver.

This commit is part of a series that aims to share more of the x86
target description creation code between GDB and gdbserver.  The
I386_LINUX_XSAVE_XCR0_OFFSET #define is used as part of the target
description creation, and I noticed that this constant is defined
separately for GDB and gdbserver.

This commit moves the definition into gdbsupport/x86-xstate.h, which
allows the #define to be shared.

There should be no user visible changes after this commit.
---
 gdb/i386-linux-tdep.h      | 20 --------------------
 gdbserver/linux-x86-low.cc | 21 ---------------------
 gdbsupport/x86-xstate.h    | 20 ++++++++++++++++++++
 3 files changed, 20 insertions(+), 41 deletions(-)

diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 5891747572b..07593c6a8ec 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -58,26 +58,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 /* Return the target description according to XCR0.  */
 extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */ 
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index ffb2a90731d..62612773a87 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -831,27 +831,6 @@ x86_target::low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
 \f
 static int use_xml;
 
-/* Format of XSAVE extended state is:
-	struct
-	{
-	  fxsave_bytes[0..463]
-	  sw_usable_bytes[464..511]
-	  xstate_hdr_bytes[512..575]
-	  avx_bytes[576..831]
-	  future_state etc
-	};
-
-  Same memory layout will be used for the coredump NT_X86_XSTATE
-  representing the XSAVE extended state registers.
-
-  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
-  extended state mask, which is the same as the extended control register
-  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
-  together with the mask saved in the xstate_hdr_bytes to determine what
-  states the processor/OS supports and what state, used or initialized,
-  the process/thread is in.  */
-#define I386_LINUX_XSAVE_XCR0_OFFSET 464
-
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index 89c1143fbe1..11b37544aa3 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -120,4 +120,24 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
 
 #define I387_MXCSR_INIT_VAL 0x1f80
 
+/* Format of XSAVE extended state is:
+	struct
+	{
+	  fxsave_bytes[0..463]
+	  sw_usable_bytes[464..511]
+	  xstate_hdr_bytes[512..575]
+	  extended state regions (AVX, MPX, AVX512, PKRU, etc.)
+	};
+
+  Same memory layout will be used for the coredump NT_X86_XSTATE
+  representing the XSAVE extended state registers.
+
+  The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+  extended state mask, which is the same as the extended control register
+  0 (the XFEATURE_ENABLED_MASK register), XCR0.  We can use this mask
+  together with the mask saved in the xstate_hdr_bytes to determine what
+  states the processor/OS supports and what state, used or initialized,
+  the process/thread is in.  */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
 #endif /* COMMON_X86_XSTATE_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 07/11] gdb/gdbserver: share some code relating to target description creation
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (5 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 06/11] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 08/11] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
                           ` (3 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

This commit is part of a series to share more of the x86 target
description creation code between GDB and gdbserver.

Unlike previous commits which were mostly refactoring, this commit is
the first that makes a real change, though that change should mostly
be for gdbserver; I've largely adopted the "GDB" way of doing things
for gdbserver, and this fixes a real gdbserver bug.

On a x86-64 Linux target, running the test:

  gdb.server/connect-with-no-symbol-file.exp

results in two core files being created.  Both of these core files are
from the inferior process, created after gdbserver has detached.

In this test a gdbserver process is started and then, after gdbserver
has started, but before GDB attaches, we either delete the inferior
executable, or change its permissions so it can't be read.  Only after
doing this do we attempt to connect with GDB.

As GDB connects to gdbserver, gdbserver attempts to figure out the
target description so that it can send the description to GDB, this
involves a call to x86_linux_read_description.

In x86_linux_read_description one of the first things we do is try to
figure out if the process is 32-bit or 64-bit.  To do this we look up
the executable via the thread-id, and then attempt to read the
architecture size from the executable.  This isn't going to work if
the executable has been deleted, or is no longer readable.

And so, as we can't read the executable, we default to an i386 target
and use an i386 target description.

A consequence of using an i386 target description is that addresses
are assumed to be 32-bits.  Here's an example session that shows the
problems this causes.  This is run on an x86-64 machine, and the test
binary (xx.x) is a standard 64-bit x86-64 binary:

  shell_1$ gdbserver --once localhost :54321 /tmp/xx.x

  shell_2$ gdb -q
  (gdb) set sysroot
  (gdb) shell chmod 000 /tmp/xx.x
  (gdb) target remote :54321
  Remote debugging using :54321
  warning: /tmp/xx.x: Permission denied.
  0xf7fd3110 in ?? ()
  (gdb) show architecture
  The target architecture is set to "auto" (currently "i386").
  (gdb) p/x $pc
  $1 = 0xf7fd3110
  (gdb) info proc mappings
  process 2412639
  Mapped address spaces:

  	Start Addr   End Addr       Size     Offset  Perms   objfile
  	  0x400000   0x401000     0x1000        0x0  r--p   /tmp/xx.x
  	  0x401000   0x402000     0x1000     0x1000  r-xp   /tmp/xx.x
  	  0x402000   0x403000     0x1000     0x2000  r--p   /tmp/xx.x
  	  0x403000   0x405000     0x2000     0x2000  rw-p   /tmp/xx.x
  	0xf7fcb000 0xf7fcf000     0x4000        0x0  r--p   [vvar]
  	0xf7fcf000 0xf7fd1000     0x2000        0x0  r-xp   [vdso]
  	0xf7fd1000 0xf7fd3000     0x2000        0x0  r--p   /usr/lib64/ld-2.30.so
  	0xf7fd3000 0xf7ff3000    0x20000     0x2000  r-xp   /usr/lib64/ld-2.30.so
  	0xf7ff3000 0xf7ffb000     0x8000    0x22000  r--p   /usr/lib64/ld-2.30.so
  	0xf7ffc000 0xf7ffe000     0x2000    0x2a000  rw-p   /usr/lib64/ld-2.30.so
  	0xf7ffe000 0xf7fff000     0x1000        0x0  rw-p
  	0xfffda000 0xfffff000    0x25000        0x0  rw-p   [stack]
  	0xff600000 0xff601000     0x1000        0x0  r-xp   [vsyscall]
  (gdb) info inferiors
    Num  Description       Connection           Executable
  * 1    process 2412639   1 (remote :54321)
  (gdb) shell cat /proc/2412639/maps
  00400000-00401000 r--p 00000000 fd:03 45907133           /tmp/xx.x
  00401000-00402000 r-xp 00001000 fd:03 45907133           /tmp/xx.x
  00402000-00403000 r--p 00002000 fd:03 45907133           /tmp/xx.x
  00403000-00405000 rw-p 00002000 fd:03 45907133           /tmp/xx.x
  7ffff7fcb000-7ffff7fcf000 r--p 00000000 00:00 0          [vvar]
  7ffff7fcf000-7ffff7fd1000 r-xp 00000000 00:00 0          [vdso]
  7ffff7fd1000-7ffff7fd3000 r--p 00000000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7fd3000-7ffff7ff3000 r-xp 00002000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ff3000-7ffff7ffb000 r--p 00022000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffc000-7ffff7ffe000 rw-p 0002a000 fd:00 143904     /usr/lib64/ld-2.30.so
  7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
  7ffffffda000-7ffffffff000 rw-p 00000000 00:00 0          [stack]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]
  (gdb)

Notice the difference between the mappings reported via GDB and those
reported directly from the kernel via /proc/PID/maps, the addresses of
every mapping is clamped to 32-bits for GDB, while the kernel reports
real 64-bit addresses.

Notice also that the $pc value is a 32-bit value.  It appears to be
within one of the mappings reported by GDB, but is outside any of the
mappings reported from the kernel.

And this is where the problem arises.  When gdbserver detaches from
the inferior we pass the inferior the address from which it should
resume.  Due to the 32/64 bit confusion we tell the inferior to resume
from the 32-bit $pc value, which is not within any valid mapping, and
so, as soon as the inferior resumes, it segfaults.

If we look at how GDB (not gdbserver) figures out its target
description then we see an interesting difference.  GDB doesn't try to
read the executable.  Instead GDB uses ptrace to query the thread's
state, and uses this to figure out the if the thread is 32 or 64 bit.

If we update gdbserver to do it the "GDB" way then the above problem
is resolved, gdbserver now sees the process as 64-bit, and when we
detach from the inferior we give it the correct 64-bit address, and
the inferior no longer segfaults.

Now, I could just update the gdbserver code, but better, I think, to
share one copy of the code between GDB and gdbserver in gdb/nat/.
That is what this commit does.

The cores of x86_linux_read_description from gdbserver and
x86_linux_nat_target::read_description from GDB are moved into a new
file gdb/nat/x86-linux-tdesc.c and combined into a single function
x86_linux_tdesc_for_tid which is called from each location.

This new function does things the GDB way, the only changes are to
allow for the sharing; we now have a callback function to call the
first time that the xcr0 state is read, this allows for GDB and
gdbserver to perform their own initialisation as needed, and
additionally, the new function takes a pointer for where to cache the
xcr0 value, this isn't needed for this commit, but will be useful in a
later commit where gdbserver will want to read this cached xcr0
value.

Another thing to note about this commit is how the functions
i386_linux_read_description and amd64_linux_read_description are
handled.  For now I've left these function as implemented separately
in GDB and gdbserver.  I've moved the declarations of these functions
into gdb/arch/{i386,amd64}-linux-tdesc.h, but the implementations are
left where they are.

A later commit in this series will make these functions shared too,
but doing this is not trivial, so I've left that for a separate
commit.  Merging the declarations as I've done here ensures that
everyone implements the function to the same API, and once these
functions are shared (in a later commit) we'll want a shared
declaration anyway.
---
 gdb/Makefile.in              |   3 +
 gdb/amd64-linux-tdep.c       |   1 +
 gdb/amd64-linux-tdep.h       |   6 --
 gdb/arch/amd64-linux-tdesc.h |  30 +++++++
 gdb/arch/i386-linux-tdesc.h  |  29 +++++++
 gdb/configure.nat            |   4 +-
 gdb/i386-linux-tdep.c        |   1 +
 gdb/i386-linux-tdep.h        |   3 -
 gdb/nat/x86-linux-tdesc.c    | 124 ++++++++++++++++++++++++++++
 gdb/nat/x86-linux-tdesc.h    |  60 ++++++++++++++
 gdb/x86-linux-nat.c          |  91 ++++-----------------
 gdbserver/configure.srv      |   2 +
 gdbserver/linux-amd64-ipa.cc |   1 +
 gdbserver/linux-i386-ipa.cc  |   1 +
 gdbserver/linux-x86-low.cc   | 151 +++++++++++------------------------
 gdbserver/linux-x86-tdesc.cc |   2 +
 gdbserver/linux-x86-tdesc.h  |   7 --
 17 files changed, 317 insertions(+), 199 deletions(-)
 create mode 100644 gdb/arch/amd64-linux-tdesc.h
 create mode 100644 gdb/arch/i386-linux-tdesc.h
 create mode 100644 gdb/nat/x86-linux-tdesc.c
 create mode 100644 gdb/nat/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 353e45b3cec..a24b2232daa 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1549,8 +1549,10 @@ HFILES_NO_SRCDIR = \
 	arch/aarch64-insn.h \
 	arch/aarch64-mte-linux.h \
 	arch/aarch64-scalable-linux.h \
+	arch/amd64-linux-tdesc.h \
 	arch/arc.h \
 	arch/arm.h \
+	arch/i386-linux-tdesc.h \
 	arch/i386.h \
 	arch/loongarch.h \
 	arch/ppc-linux-common.h \
@@ -1606,6 +1608,7 @@ HFILES_NO_SRCDIR = \
 	nat/x86-gcc-cpuid.h \
 	nat/x86-linux.h \
 	nat/x86-linux-dregs.h \
+	nat/x86-linux-tdesc.h \
 	python/py-event.h \
 	python/py-events.h \
 	python/py-stopevent.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 9d560ac4fbf..bcb9868e79e 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -41,6 +41,7 @@
 #include "arch/amd64.h"
 #include "target-descriptions.h"
 #include "expop.h"
+#include "arch/amd64-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h
index 2003dcda78f..0ec49e7fe03 100644
--- a/gdb/amd64-linux-tdep.h
+++ b/gdb/amd64-linux-tdep.h
@@ -43,12 +43,6 @@ extern struct target_desc *tdesc_x32_linux;
 extern struct target_desc *tdesc_x32_avx_linux;
 extern struct target_desc *tdesc_x32_avx_avx512_linux;
 
-/* Return the right amd64-linux target descriptions according to
-   XCR0_FEATURES_BIT and IS_X32.  */
-
-const target_desc *amd64_linux_read_description (uint64_t xcr0_features_bit,
-						 bool is_x32);
-
 /* Enum that defines the syscall identifiers for amd64 linux.
    Used for process record/replay, these will be translated into
    a gdb-canonical set of syscall ids in linux-record.c.  */
diff --git a/gdb/arch/amd64-linux-tdesc.h b/gdb/arch/amd64-linux-tdesc.h
new file mode 100644
index 00000000000..db425b60df6
--- /dev/null
+++ b/gdb/arch/amd64-linux-tdesc.h
@@ -0,0 +1,30 @@
+/*  Target description related code for GNU/Linux x86-64.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_AMD64_LINUX_TDESC_H
+#define ARCH_AMD64_LINUX_TDESC_H
+
+struct target_desc;
+
+/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
+
+extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
+							bool is_x32);
+
+#endif /* ARCH_AMD64_LINUX_TDESC_H */
diff --git a/gdb/arch/i386-linux-tdesc.h b/gdb/arch/i386-linux-tdesc.h
new file mode 100644
index 00000000000..0b736337a75
--- /dev/null
+++ b/gdb/arch/i386-linux-tdesc.h
@@ -0,0 +1,29 @@
+/* Target description related code for GNU/Linux i386.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_I386_LINUX_TDESC_H
+#define ARCH_I386_LINUX_TDESC_H
+
+struct target_desc;
+
+/* Return the i386 target description corresponding to XCR0.  */
+
+extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+
+#endif /* ARCH_I386_LINUX_TDESC_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 8b98511cef7..4bcc0696027 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -322,7 +322,7 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o \
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
 		nat/amd64-linux-siginfo.o"
 		;;
 	    sparc)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 44730f204db..78ebc99d3df 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -39,6 +39,7 @@
 
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
+#include "arch/i386-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 07593c6a8ec..e8691cd778e 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -55,9 +55,6 @@ extern void i386_linux_report_signal_info (struct gdbarch *gdbarch,
 					   struct ui_out *uiout,
 					   enum gdb_signal siggnal);
 
-/* Return the target description according to XCR0.  */
-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 extern int i386_linux_gregset_reg_offset[];
 
 /* Return x86 siginfo type.  */
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
new file mode 100644
index 00000000000..c2301e6a873
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -0,0 +1,124 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "nat/x86-linux-tdesc.h"
+#ifdef __x86_64__
+#include "arch/amd64.h"
+#include "arch/amd64-linux-tdesc.h"
+#endif
+#include "arch/i386.h"
+#include "arch/i386-linux-tdesc.h"
+
+#include "nat/x86-linux.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/x86-xstate.h"
+
+#ifndef __x86_64__
+#include <sys/procfs.h>
+#endif
+
+#include <sys/uio.h>
+#include <elf.h>
+
+#ifndef IN_PROCESS_AGENT
+
+/* See nat/x86-linux-tdesc.h.  */
+
+const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage)
+{
+#ifdef __x86_64__
+
+  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+  bool is_64bit = arch_size.is_64bit ();
+  bool is_x32 = arch_size.is_x32 ();
+
+  if (sizeof (void *) == 4 && is_64bit && !is_x32)
+    error ("%s", error_msg);
+
+#elif HAVE_PTRACE_GETFPXREGS
+  if (have_ptrace_getfpxregs == -1)
+    {
+      elf_fpxregset_t fpxregs;
+
+      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+	{
+	  have_ptrace_getfpxregs = 0;
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
+	}
+    }
+#endif
+
+  if (*have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    {
+      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, tid,
+		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
+	{
+	  *have_ptrace_getregset = TRIBOOL_FALSE;
+	  *xcr0_storage = 0;
+	}
+      else
+	{
+	  *have_ptrace_getregset = TRIBOOL_TRUE;
+
+	  /* Get XCR0 from XSAVE extended state.  */
+	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+				      / sizeof (uint64_t))];
+
+#ifdef __x86_64__
+	  /* No MPX on x32.  */
+	  if (is_64bit && is_x32)
+	    *xcr0_storage &= ~X86_XSTATE_MPX;
+#endif /* __x86_64__ */
+
+	  xcr0_init_cb (*xcr0_storage);
+	}
+    }
+
+  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
+     PTRACE_GETREGSET is not available then set xcr0_features_bits to
+     zero so that the "no-features" descriptions are returned by the
+     switches below.  */
+  uint64_t xcr0_features_bits;
+  if (*have_ptrace_getregset == TRIBOOL_TRUE)
+    xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+  else
+    xcr0_features_bits = 0;
+
+#ifdef __x86_64__
+  if (is_64bit)
+    {
+      return amd64_linux_read_description (xcr0_features_bits, is_x32);
+    }
+  else
+#endif
+    return i386_linux_read_description (xcr0_features_bits);
+}
+
+#endif /* !IN_PROCESS_AGENT */
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
new file mode 100644
index 00000000000..8c481609876
--- /dev/null
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -0,0 +1,60 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_X86_LINUX_TDESC_H
+#define NAT_X86_LINUX_TDESC_H
+
+#include "gdbsupport/function-view.h"
+
+struct target_desc;
+
+/* Return the target description for Linux thread TID.
+
+   When *HAVE_PTRACE_GETREGSET is TRIBOOL_UNKNOWN then the current value of
+   xcr0 is read using ptrace calls and stored into *XCR0_STORAGE.  Then
+   XCR0_INIT_CB is called with the value of *XCR0_STORAGE and
+   *HAVE_PTRACE_GETREGSET is set to TRIBOOL_TRUE.
+
+   If the attempt to read xcr0 using ptrace fails then *XCR0_STORAGE is set
+   to zero and *HAVE_PTRACE_GETREGSET is set to TRIBOOL_FALSE.
+
+   The storage pointed to by XCR0_STORAGE must exist until the program
+   terminates, this storage is used to cache the xcr0 value.  As such
+   XCR0_INIT_CB will only be called once if xcr0 is successfully read using
+   ptrace, or not at all if the ptrace call fails.
+
+   This function returns a target description based on the extracted xcr0
+   value along with other characteristics of the thread identified by TID.
+
+   This function can return nullptr if we encounter a machine configuration
+   for which a target_desc cannot be created.  Ideally this would not be
+   the case, we should be able to create a target description for every
+   possible machine configuration.  See amd64_linux_read_description and
+   i386_linux_read_description for cases when nullptr might be
+   returned.
+
+   ERROR_MSG is using in an error() call if we try to create a target
+   description for a 64-bit process but this is a 32-bit build of GDB.  */
+
+extern const target_desc *
+x86_linux_tdesc_for_tid (int tid, enum tribool *have_ptrace_getregset,
+			 gdb::function_view<void (uint64_t)> xcr0_init_cb,
+			 const char *error_msg, uint64_t *xcr0_storage);
+
+#endif /* NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index 12965c7a21b..9fbccb28d21 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -38,6 +38,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "nat/linux-ptrace.h"
+#include "nat/x86-linux-tdesc.h"
 
 /* linux_nat_target::low_new_fork implementation.  */
 
@@ -92,90 +93,26 @@ x86_linux_nat_target::post_startup_inferior (ptid_t ptid)
 const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
+  static uint64_t xcr0_storage;
 
   if (inferior_ptid == null_ptid)
     return this->beneath ()->read_description ();
 
-  tid = inferior_ptid.pid ();
-
-#ifdef __x86_64__
-
-  x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
-  is_64bit = arch_size.is_64bit ();
-  is_x32 = arch_size.is_x32 ();
-
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87_MASK);
-	}
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  m_xsave_layout = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset == TRIBOOL_TRUE)
-    xcr0_features_bits = xcr0 & X86_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      return amd64_linux_read_description (xcr0_features_bits, is_x32);
-#endif
-    }
-  else
-    {
-      const struct target_desc * tdesc
-	= i386_linux_read_description (xcr0_features_bits);
+  int tid = inferior_ptid.pid ();
 
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE_MASK);
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDB");
 
-      return tdesc;
-    }
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state.  */
+  auto cb = [&] (uint64_t xcr0)
+  {
+    this->m_xsave_layout
+      = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+  };
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb,
+				  error_msg, &xcr0_storage);
 }
 \f
 
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 9e861a75088..7a2702d78bf 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -109,6 +109,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
@@ -371,6 +372,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
+			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index a6346750f49..df5e6aca081 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -22,6 +22,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "arch/amd64-linux-tdesc.h"
 
 /* fast tracepoints collect registers.  */
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 8f14e0937d4..aa346fc9bc3 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -22,6 +22,7 @@
 #include "tracepoint.h"
 #include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
+#include "arch/i386-linux-tdesc.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 62612773a87..68d2f13537c 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -29,8 +29,11 @@
 
 #ifdef __x86_64__
 #include "nat/amd64-linux-siginfo.h"
+#include "arch/amd64-linux-tdesc.h"
 #endif
 
+#include "arch/i386-linux-tdesc.h"
+
 #include "gdb_proc_service.h"
 /* Don't include elf/common.h if linux/elf.h got included by
    gdb_proc_service.h.  */
@@ -46,6 +49,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "linux-x86-tdesc.h"
+#include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
 static target_desc_up tdesc_amd64_linux_no_xml;
@@ -831,32 +835,20 @@ x86_target::low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
 \f
 static int use_xml;
 
+/* Cached xcr0 value.  This is initialised the first time
+   x86_linux_read_description is called.  */
+
+static uint64_t xcr0_storage;
+
 /* Get Linux/x86 target description from running target.  */
 
 static const struct target_desc *
 x86_linux_read_description (void)
 {
-  unsigned int machine;
-  int is_elf64;
-  int xcr0_features;
-  int tid;
-  static uint64_t xcr0;
-  static int xsave_len;
-  struct regset_info *regset;
-
-  tid = lwpid_of (current_thread);
-
-  is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+  int tid = lwpid_of (current_thread);
 
-  if (sizeof (void *) == 4)
-    {
-      if (is_elf64 > 0)
-       error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
-      else if (machine == EM_X86_64)
-       error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
-    }
+  const char *error_msg
+    = _("Can't debug 64-bit process with 32-bit GDBserver");
 
   /* If we are not allowed to send an XML target description then we need
      to use the hard-wired target descriptions.  This corresponds to GDB's
@@ -866,103 +858,54 @@ x86_linux_read_description (void)
      generate some alternative target descriptions.  */
   if (!use_xml)
     {
+      x86_linux_arch_size arch_size = x86_linux_ptrace_get_arch_size (tid);
+      bool is_64bit = arch_size.is_64bit ();
+      bool is_x32 = arch_size.is_x32 ();
+
+      if (sizeof (void *) == 4 && is_64bit && !is_x32)
+	error ("%s", error_msg);
+
 #ifdef __x86_64__
-      if (machine == EM_X86_64)
+      if (is_64bit && !is_x32)
 	return tdesc_amd64_linux_no_xml.get ();
       else
 #endif
 	return tdesc_i386_linux_no_xml.get ();
     }
 
-#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
-  if (machine == EM_386 && have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
-	{
-	  have_ptrace_getfpxregs = 0;
-	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  return i386_linux_read_description (X86_XSTATE_X87);
-	}
-      else
-	have_ptrace_getfpxregs = 1;
-    }
-#endif
-
-  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
-    {
-      uint64_t xstateregs[(X86_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-		  (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	have_ptrace_getregset = TRIBOOL_FALSE;
-      else
-	{
-	  have_ptrace_getregset = TRIBOOL_TRUE;
-
-	  /* Get XCR0 from XSAVE extended state.  */
-	  xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-			     / sizeof (uint64_t))];
-
-	  /* No MPX on x32.  */
-	  if (machine == EM_X86_64 && !is_elf64)
-	    xcr0 &= ~X86_XSTATE_MPX;
-
-	  xsave_len = x86_xsave_length ();
-
-	  /* Use PTRACE_GETREGSET if it is available.  */
-	  for (regset = x86_regsets;
-	       regset->fill_function != NULL; regset++)
-	    if (regset->get_request == PTRACE_GETREGSET)
-	      regset->size = xsave_len;
-	    else if (regset->type != GENERAL_REGS)
-	      regset->size = 0;
-	}
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  */
-  xcr0_features = (have_ptrace_getregset == TRIBOOL_TRUE
-		   && (xcr0 & X86_XSTATE_ALL_MASK));
-
-  if (xcr0_features)
-    i387_set_xsave_mask (xcr0, xsave_len);
+  /* Callback that is triggered the first time x86_linux_tdesc_for_tid
+     reads the xcr0 register.  Setup other bits of state */
+  auto cb = [] (uint64_t xcr0)
+  {
+    i387_set_xsave_mask (xcr0, x86_xsave_length ());
+  };
 
-  if (machine == EM_X86_64)
-    {
-#ifdef __x86_64__
-      const target_desc *tdesc = NULL;
+  /* If have_ptrace_getregset is changed to true by calling
+     x86_linux_tdesc_for_tid then we will perform some additional
+     initialisation.  */
+  bool have_ptrace_getregset_is_unknown
+    = have_ptrace_getregset == TRIBOOL_UNKNOWN;
 
-      if (xcr0_features)
-	{
-	  tdesc = amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
-						!is_elf64);
-	}
+  const target_desc *tdesc
+    = x86_linux_tdesc_for_tid (tid, &have_ptrace_getregset, cb, error_msg,
+			       &xcr0_storage);
 
-      if (tdesc == NULL)
-	tdesc = amd64_linux_read_description (X86_XSTATE_SSE_MASK, !is_elf64);
-      return tdesc;
-#endif
-    }
-  else
+  if (have_ptrace_getregset_is_unknown
+      && have_ptrace_getregset == TRIBOOL_TRUE)
     {
-      const target_desc *tdesc = NULL;
-
-      if (xcr0_features)
-	  tdesc = i386_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK);
-
-      if (tdesc == NULL)
-	tdesc = i386_linux_read_description (X86_XSTATE_SSE);
-
-      return tdesc;
+      int xsave_len = x86_xsave_length ();
+
+      /* Use PTRACE_GETREGSET if it is available.  */
+      for (regset_info *regset = x86_regsets;
+	   regset->fill_function != nullptr;
+	   regset++)
+	if (regset->get_request == PTRACE_GETREGSET)
+	  regset->size = xsave_len;
+	else if (regset->type != GENERAL_REGS)
+	  regset->size = 0;
     }
 
-  gdb_assert_not_reached ("failed to return tdesc");
+  return tdesc;
 }
 
 /* Update all the target description of all processes; a new GDB
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index cd3b5d80e37..af3735aa895 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -23,8 +23,10 @@
 #include "gdbsupport/x86-xstate.h"
 #ifdef __x86_64__
 #include "arch/amd64.h"
+#include "arch/amd64-linux-tdesc.h"
 #endif
 #include "x86-tdesc.h"
+#include "arch/i386-linux-tdesc.h"
 
 /* Return the right x86_linux_tdesc index for a given XCR0.  Return
    X86_TDESC_LAST if can't find a match.  */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index f9561b129ae..576aaf5e165 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -46,11 +46,4 @@ int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
 
 const struct target_desc *i386_get_ipa_tdesc (int idx);
 
-#ifdef __x86_64__
-const struct target_desc *amd64_linux_read_description (uint64_t xcr0,
-							bool is_x32);
-#endif
-
-const struct target_desc *i386_linux_read_description (uint64_t xcr0);
-
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 08/11] gdb/arch: assert that X86_XSTATE_MPX is not set for x32
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (6 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 07/11] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 09/11] gdbserver: update target description creation for x86/linux Andrew Burgess
                           ` (2 subsequent siblings)
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

While rebasing this series past this commit:

  commit 4bb20a6244b7091a9a7a2ae35dfbd7e8db27550a
  Date:   Wed Mar 20 04:13:18 2024 -0700

      gdbserver: Clear X86_XSTATE_MPX bits in xcr0 on x32

I worried that there could be other paths that might result in an xcr0
value which has X86_XSTATE_MPX set in x32 mode.  As everyone
eventually calls amd64_create_target_description to build their target
description, I figured we could assert in here that if X86_XSTATE_MPX
is set then we should not be an x32 target, this will uncover any
other bugs in this area.

I'm not currently able to build/run any x32 binaries, so I have no way
to test this, but the author of commit 4bb20a6244b7091 did test this
series with that assert in place and didn't see any problems.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31511
---
 gdb/arch/amd64.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index cb9683c6931..94d55d72a4e 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -65,8 +65,12 @@ amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
   if (xcr0 & X86_XSTATE_AVX)
     regnum = create_feature_i386_64bit_avx (tdesc.get (), regnum);
 
-  if ((xcr0 & X86_XSTATE_MPX) && !is_x32)
-    regnum = create_feature_i386_64bit_mpx (tdesc.get (), regnum);
+  if (xcr0 & X86_XSTATE_MPX)
+    {
+      /* MPX is not available on x32.  */
+      gdb_assert (!is_x32);
+      regnum = create_feature_i386_64bit_mpx (tdesc.get (), regnum);
+    }
 
   if (xcr0 & X86_XSTATE_AVX512)
     regnum = create_feature_i386_64bit_avx512 (tdesc.get (), regnum);
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 09/11] gdbserver: update target description creation for x86/linux
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (7 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 08/11] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 10/11] gdb: move xcr0 == 0 check into i386_linux_core_read_description Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 11/11] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

This commit is part of a series which aims to share more of the target
description creation between GDB and gdbserver for x86/Linux.

After some refactoring earlier in this series the shared
x86_linux_tdesc_for_tid function was added into nat/x86-linux-tdesc.c.
However, this function still relies on amd64_linux_read_description
and i386_linux_read_description which are implemented separately for
both gdbserver and GDB.  Given that at their core, all these functions
do is:

  1. take an xcr0 value as input,
  2. mask out some feature bits,
  3. look for a cached pre-generated target description and return it
     if found,
  4. if no cached target description is found then call either
     amd64_create_target_description or
     i386_create_target_description to create a new target
     description, which is then added to the cache.  Return the newly
     created target description.

The inner functions amd64_create_target_description and
i386_create_target_description are already shared between GDB and
gdbserver (in the gdb/arch/ directory), so the only thing that
the *_read_description functions really do is add the caching layer,
and it feels like this really could be shared.

However, we have a small problem.

On the GDB side we create target descriptions using a different set of
cpu features than on the gdbserver side!  This means that for the
exact same target, we might get a different target description when
using native GDB vs using gdbserver.  This surely feels like a
mistake, I would expect to get the same target description on each.

The table below shows the number of possible different target
descriptions that we can create on the GDB side vs on the gdbserver
side for each target type:

        | GDB | gdbserver
  ------|-----|----------
  i386  | 64  | 7
  amd64 | 32  | 7
  x32   | 16  | 7

So in theory, all I want to do is move the GDB version
of *_read_description into the arch/ directory and have gdbserver use
that, then both GDB and gdbserver would be able to create any of the
possible target descriptions.

Unfortunately it's a little more complex than that due to the in
process agent (IPA).

When the IPA is in use, gdbserver sends a target description index to
the IPA, and the IPA uses this to find the correct target description
to use.

** START OF AN ASIDE **

Back in the day I suspect this approach made perfect sense.  However
since this commit:

  commit a8806230241d201f808d856eaae4d44088117b0c
  Date:   Thu Dec 7 17:07:01 2017 +0000

      Initialize target description early in IPA

I think passing the index is now more trouble than its worth.

We used to pass the index, and then use that index to lookup which
target description to instantiate and use.  However, the above commit
fixed an issue where we can't call malloc() within (certain parts of)
the IPA (apparently), so instead we now pre-compute _every_ possible
target description within the IPA.  The index is now only used to
lookup which of the (many) pre-computed target descriptions to use.

It would (I think) have been easier all around if the IPA just
self-inspected, figured out its own xcr0 value, and used that to
create the one target description that is required.  So long as the
xcr0 to target description code is shared (at compile time) with
gdbserver, then we can be sure that the IPA will derive the same
target description as gdbserver, and we would avoid all this index
passing business, which has made this commit so very, very painful.

** END OF AN ASIDE **

Currently then for x86/linux, gdbserver sends a number between 0 and 7
to the IPA, and the IPA uses this to create a target description.

However, I am proposing that gdbserver should now create one of (up
to) 64 different target descriptions for i386, so this 0 to 7 index
isn't going to be good enough any more (amd64 and x32 have slightly
fewer possible target descriptions, but still more than 8, so the
problem is the same).

For a while I wondered if I was going to have to try and find some
backward compatible solution for this mess.  But after seeing how
lightly the IPA is actually documented, I wonder if it is not the case
that there is a tight coupling between a version of gdbserver and a
version of the IPA?  At least I'm hoping so.

In this commit I have thrown out the old IPA target description index
numbering scheme, and switched to a completely new numbering scheme.
Instead of the index that is passed being arbitrary, the index is
instead calculated from the set of cpu features that are present on
the target.  Within the IPA we can then reverse this logic to recreate
the xcr0 value based on the index, and from the xcr0 value we can
choose the correct target description.

With the gdbserver to IPA numbering scheme issue resolved I have then
update the gdbserver versions of amd64_linux_read_description and
i386_linux_read_description so that they create target descriptions
using the same set of cpu features as GDB itself.

After this gdbserver should now always come up with the same target
description as GDB does on any x86/Linux target.

This commit does not introduce any new code sharing between GDB and
gdbserver as previous commits in this series have done.  Instead this
commit is all about bringing GDB and gdbserver into alignment
functionally so that the next commit(s) can merge the GDB and
gdbserver versions of these functions.

Approved-By: John Baldwin <jhb@FreeBSD.org>
---
 gdbserver/linux-amd64-ipa.cc |  43 +----
 gdbserver/linux-i386-ipa.cc  |  23 +--
 gdbserver/linux-x86-low.cc   |  15 +-
 gdbserver/linux-x86-tdesc.cc | 316 +++++++++++++++++++++++++----------
 gdbserver/linux-x86-tdesc.h  |  49 +++---
 5 files changed, 278 insertions(+), 168 deletions(-)

diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index df5e6aca081..0c80812cc6f 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -164,47 +164,21 @@ supply_static_tracepoint_registers (struct regcache *regcache,
 
 #endif /* HAVE_UST */
 
-#if !defined __ILP32__
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-#endif
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
 
 #if defined __ILP32__
-  switch (idx)
-    {
-    case X86_TDESC_SSE:
-      return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-    case X86_TDESC_AVX:
-      return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-    case X86_TDESC_AVX_AVX512:
-      return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
-    default:
-      break;
-    }
+  bool is_x32 = true;
 #else
-  return amd64_linux_read_description (idx2mask[idx], false);
+  bool is_x32 = false;
 #endif
 
-  internal_error ("unknown ipa tdesc index: %d", idx);
+  return amd64_linux_read_description (xcr0, is_x32);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a
@@ -272,11 +246,10 @@ void
 initialize_low_tracepoint (void)
 {
 #if defined __ILP32__
-  amd64_linux_read_description (X86_XSTATE_SSE_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_MASK, true);
-  amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true);
+  for (auto i = 0; i < x86_linux_x32_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
 #else
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    amd64_linux_read_description (idx2mask[i], false);
+  for (auto i = 0; i < x86_linux_amd64_tdesc_count (); i++)
+    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
 #endif
 }
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index aa346fc9bc3..c1c3152fb04 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -245,28 +245,15 @@ initialize_fast_tracepoint_trampoline_buffer (void)
     }
 }
 
-/* Map the tdesc index to xcr0 mask.  */
-static uint64_t idx2mask[X86_TDESC_LAST] = {
-  X86_XSTATE_X87_MASK,
-  X86_XSTATE_SSE_MASK,
-  X86_XSTATE_AVX_MASK,
-  X86_XSTATE_MPX_MASK,
-  X86_XSTATE_AVX_MPX_MASK,
-  X86_XSTATE_AVX_AVX512_MASK,
-  X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
-};
-
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  if (idx >= X86_TDESC_LAST)
-    {
-      internal_error ("unknown ipa tdesc index: %d", idx);
-    }
-  return i386_linux_read_description (idx2mask[idx]);
+  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+
+  return i386_linux_read_description (xcr0);
 }
 
 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
@@ -288,6 +275,6 @@ void
 initialize_low_tracepoint (void)
 {
   initialize_fast_tracepoint_trampoline_buffer ();
-  for (auto i = 0; i < X86_TDESC_LAST; i++)
-    i386_linux_read_description (idx2mask[i]);
+  for (auto i = 0; i < x86_linux_i386_tdesc_count (); i++)
+    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
 }
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 68d2f13537c..6e23a53118b 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -2882,14 +2882,17 @@ x86_target::get_ipa_tdesc_idx ()
   struct regcache *regcache = get_thread_regcache (current_thread, 0);
   const struct target_desc *tdesc = regcache->tdesc;
 
+  if (!use_xml)
+    {
+      if (tdesc == tdesc_i386_linux_no_xml.get ()
 #ifdef __x86_64__
-  return amd64_get_ipa_tdesc_idx (tdesc);
-#endif
-
-  if (tdesc == tdesc_i386_linux_no_xml.get ())
-    return X86_TDESC_SSE;
+	  || tdesc == tdesc_amd64_linux_no_xml.get ()
+#endif /* __x86_64__ */
+	  )
+	return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+    }
 
-  return i386_get_ipa_tdesc_idx (tdesc);
+  return x86_linux_xcr0_to_tdesc_idx (xcr0_storage);
 }
 
 /* The linux target ops object.  */
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index af3735aa895..5e12526bf17 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -28,96 +28,278 @@
 #include "x86-tdesc.h"
 #include "arch/i386-linux-tdesc.h"
 
-/* Return the right x86_linux_tdesc index for a given XCR0.  Return
-   X86_TDESC_LAST if can't find a match.  */
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
 
-static enum x86_linux_tdesc
-xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32)
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask ()
 {
-  if (xcr0 & X86_XSTATE_PKRU)
-    {
-      if (is_x32)
-	{
-	  /* No x32 MPX and PKU, fall back to avx_avx512.  */
-	  return X86_TDESC_AVX_AVX512;
-	}
-      else
-	return X86_TDESC_AVX_MPX_AVX512_PKU;
-    }
-  else if (xcr0 & X86_XSTATE_AVX512)
-    return X86_TDESC_AVX_AVX512;
-  else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK)
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_amd64_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count_1 ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_x32_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count_1 ();
+}
+
+/* See linux-x86-tdesc.h.  */
+
+int
+x86_linux_i386_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count_1 ();
+}
+
+#endif /* IN_PROCESS_AGENT */
+
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_AVX_MPX;
+     if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
+	  == x86_linux_all_tdesc_features[i].feature)
+	idx |= (1 << i);
     }
-  else if (xcr0 & X86_XSTATE_MPX)
+
+  return idx;
+}
+
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
     {
-      if (is_x32) /* No MPX on x32.  */
-	return X86_TDESC_AVX;
-      else
-	return X86_TDESC_MPX;
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
     }
-  else if (xcr0 & X86_XSTATE_AVX)
-    return X86_TDESC_AVX;
-  else if (xcr0 & X86_XSTATE_SSE)
-    return X86_TDESC_SSE;
-  else if (xcr0 & X86_XSTATE_X87)
-    return X86_TDESC_MMX;
-  else
-    return X86_TDESC_LAST;
+
+  return xcr0;
 }
 
+#endif /* IN_PROCESS_AGENT */
+
 #if defined __i386__ || !defined IN_PROCESS_AGENT
 
-static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible i386 target descriptions.  */
 
-/* Return the target description according to XCR0.  */
+static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count_1 ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 i386_linux_read_description (uint64_t xcr0)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false);
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count_1 ());
 
-  struct target_desc **tdesc = &i386_tdescs[idx];
+  target_desc **tdesc = &i386_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = i386_create_target_description (xcr0, true, false);
 
       init_target_desc (*tdesc, i386_expedite_regs);
     }
 
-  return *tdesc;;
+  return *tdesc;
 }
 #endif
 
 #ifdef __x86_64__
 
-static target_desc *amd64_tdescs[X86_TDESC_LAST] = { };
-static target_desc *x32_tdescs[X86_TDESC_LAST] = { };
+/* A cache of all possible amd64 target descriptions.  */
+
+static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count_1 ()] = { };
+
+/* A cache of all possible x32 target descriptions.  */
+
+static target_desc *x32_tdescs[x86_linux_x32_tdesc_count_1 ()] = { };
+
+/* See nat/x86-linux-tdesc.h.  */
 
 const struct target_desc *
 amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 {
-  enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32);
+  if (is_x32)
+    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  else
+    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
+
+  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
 
-  if (idx == X86_TDESC_LAST)
-    return NULL;
+  if (is_x32)
+    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count_1 ());
+  else
+    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count_1 ());
 
-  struct target_desc **tdesc = NULL;
+  target_desc **tdesc = nullptr;
 
   if (is_x32)
     tdesc = &x32_tdescs[idx];
   else
     tdesc = &amd64_tdescs[idx];
 
-  if (*tdesc == NULL)
+  if (*tdesc == nullptr)
     {
       *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
 
@@ -127,39 +309,3 @@ amd64_linux_read_description (uint64_t xcr0, bool is_x32)
 }
 
 #endif
-
-#ifndef IN_PROCESS_AGENT
-
-int
-i386_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == i386_tdescs[i])
-	return i;
-    }
-
-  /* If none tdesc is found, return the one with minimum features.  */
-  return X86_TDESC_MMX;
-}
-
-#if defined __x86_64__
-int
-amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc)
-{
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == amd64_tdescs[i])
-	return i;
-    }
-  for (int i = 0; i < X86_TDESC_LAST; i++)
-    {
-      if (tdesc == x32_tdescs[i])
-	return i;
-    }
-
-  return X86_TDESC_SSE;
-}
-
-#endif
-#endif
diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h
index 576aaf5e165..3d6e0e51833 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdbserver/linux-x86-tdesc.h
@@ -21,29 +21,30 @@
 #ifndef GDBSERVER_LINUX_X86_TDESC_H
 #define GDBSERVER_LINUX_X86_TDESC_H
 
-/* Note: since IPA obviously knows what ABI it's running on (i386 vs x86_64
-   vs x32), it's sufficient to pass only the register set here.  This,
-   together with the ABI known at IPA compile time, maps to a tdesc.  */
-
-enum x86_linux_tdesc {
-  X86_TDESC_MMX = 0,
-  X86_TDESC_SSE = 1,
-  X86_TDESC_AVX = 2,
-  X86_TDESC_MPX = 3,
-  X86_TDESC_AVX_MPX = 4,
-  X86_TDESC_AVX_AVX512 = 5,
-  X86_TDESC_AVX_MPX_AVX512_PKU = 6,
-  X86_TDESC_LAST = 7,
-};
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-int i386_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-#if defined __x86_64__ && !defined IN_PROCESS_AGENT
-int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc);
-#endif
-
-const struct target_desc *i386_get_ipa_tdesc (int idx);
+/* Convert an xcr0 value into an integer.  The integer will be passed to
+   the in-process-agent where it will then be passed to
+   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+#ifdef IN_PROCESS_AGENT
+
+/* 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+
+/* Within the in-process-agent we need to pre-initialise all of the target
+   descriptions, to do this we need to know how many target descriptions
+   there are for each different target type.  These functions return the
+   target description count for the relevant target.  */
+
+extern int x86_linux_amd64_tdesc_count ();
+extern int x86_linux_x32_tdesc_count ();
+extern int x86_linux_i386_tdesc_count ();
+
+
+#endif /* IN_PROCESS_AGENT */
 
 #endif /* GDBSERVER_LINUX_X86_TDESC_H */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 10/11] gdb: move xcr0 == 0 check into i386_linux_core_read_description
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (8 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 09/11] gdbserver: update target description creation for x86/linux Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  2024-04-26 15:01         ` [PATCHv5 11/11] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

Currently, in i386_linux_core_read_description, if GDB fails to
extract an xcr0 value from the core file, then we will have a default
zero value for the xcr0 variable, we still call the
i386_linux_read_description function, which checks for this zero value
and returns nullptr.

Back in i386_linux_core_read_description we spot the nullptr return
value from i386_linux_read_description and call
i386_linux_read_description again, but this time passing a default
value for xcr0.

In the next commit I plan to rework i386_linux_read_description, and
in so doing I will remove the check for xcr0 == 0, this is inline with
how the amd64 code is written.

However, this means that the 'xcr0 == 0' check needs to move up the
stack to i386_linux_core_read_description, again, this brings the i386
code into line with the amd64 code.

This is just a refactor in preparation for the next commit, there
should be no user visible changes after this commit.
---
 gdb/i386-linux-tdep.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 78ebc99d3df..c796f87780b 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -714,15 +714,16 @@ i386_linux_core_read_description (struct gdbarch *gdbarch,
   /* Linux/i386.  */
   x86_xsave_layout layout;
   uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
-  const struct target_desc *tdesc = i386_linux_read_description (xcr0);
 
-  if (tdesc != NULL)
-    return tdesc;
+  if (xcr0 == 0)
+    {
+      if (bfd_get_section_by_name (abfd, ".reg-xfp") != nullptr)
+	xcr0 = X86_XSTATE_SSE_MASK;
+      else
+	xcr0 = X86_XSTATE_X87_MASK;
+    }
 
-  if (bfd_get_section_by_name (abfd, ".reg-xfp") != NULL)
-    return i386_linux_read_description (X86_XSTATE_SSE_MASK);
-  else
-    return i386_linux_read_description (X86_XSTATE_X87_MASK);
+  return i386_linux_read_description (xcr0);
 }
 
 /* Similar to i386_supply_fpregset, but use XSAVE extended state.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 11/11] gdb/gdbserver: share x86/linux tdesc caching
  2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
                           ` (9 preceding siblings ...)
  2024-04-26 15:01         ` [PATCHv5 10/11] gdb: move xcr0 == 0 check into i386_linux_core_read_description Andrew Burgess
@ 2024-04-26 15:01         ` Andrew Burgess
  10 siblings, 0 replies; 65+ messages in thread
From: Andrew Burgess @ 2024-04-26 15:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, felix.willgerodt, John Baldwin

This commit builds on the previous series of commits to share the
target description caching code between GDB and gdbserver for
x86/Linux targets.

The objective of this commit is to move the four functions (2 each of)
i386_linux_read_description and amd64_linux_read_description into the
gdb/arch/ directory and combine them so we have just a single copy of
each.  Then GDB, gdbserver, and the in-process-agent (IPA) will link
against these shared functions.

It is worth reading the description of the previous commit(s) to see
why this merging is not as simple as it seems, but in summary,
gdbserver used to generate a different set of possible target
descriptions than GDB.  The previous commit(s) fixed this, so now it
should be simpler to share the functions.

One curiosity with this patch is the function
x86_linux_post_init_tdesc.  On the gdbserver side the two functions
amd64_linux_read_description and i386_linux_read_description have some
functionality that is not present on the GDB side, that is some
additional configuration that is performed as each target description
is created to setup the expedited registers.

To support this I've added the function x86_linux_post_init_tdesc.
This function is called from the two *_linux_read_description
functions, but is implemented separately for GDB and gdbserver.

An alternative approach that avoids adding x86_linux_post_init_tdesc
would be to have x86_linux_tdesc_for_tid return a non-const target
description, in x86_target::low_arch_setup we could then inspect the
target description to figure out if it is 64-bit or not, and modify
the target description as needed.  In the end I figured that adding
the x86_linux_post_init_tdesc function was good enough, so went with
that solution.

The contents of gdbserver/linux-x86-low.cc have moved to
gdb/arch/x86-linux-tdesc-features.c, and gdbserver/linux-x86-tdesc.h
has moved to gdb/arch/x86-linux-tdesc-features.h, this change leads to
some updates in the #includes in the gdbserver/ directory.

For testing I've done the following:

  - Built on x86-64 GNU/Linux for all targets, and just for the native
    target,

  - Build on i386 GNU/Linux for all targets, and just for the native
    target,

  - Build on a 64-bit, non-x86 GNU/Linux for all targets, just for the
    native target, and for targets x86_64-*-linux and i386-*-linux.

This did flush out a bunch of build issues where I'd failed to add a
required file in a configure.* file, but I think everything should now
be good.
---
 gdb/Makefile.in                               |   5 +
 gdb/amd64-linux-tdep.c                        |  31 --
 gdb/arch/amd64-linux-tdesc.c                  |  61 ++++
 gdb/arch/i386-linux-tdesc.c                   |  51 +++
 gdb/arch/x86-linux-tdesc-features.c           | 247 +++++++++++++++
 .../arch/x86-linux-tdesc-features.h           |  50 +--
 gdb/arch/x86-linux-tdesc.h                    |  37 +++
 gdb/configure.nat                             |   6 +-
 gdb/configure.tgt                             |  11 +-
 gdb/i386-linux-tdep.c                         |  26 +-
 gdbserver/configure.srv                       |   9 +
 gdbserver/linux-amd64-ipa.cc                  |   2 +-
 gdbserver/linux-i386-ipa.cc                   |   2 +-
 gdbserver/linux-x86-low.cc                    |   2 +-
 gdbserver/linux-x86-tdesc.cc                  | 292 +-----------------
 15 files changed, 469 insertions(+), 363 deletions(-)
 create mode 100644 gdb/arch/amd64-linux-tdesc.c
 create mode 100644 gdb/arch/i386-linux-tdesc.c
 create mode 100644 gdb/arch/x86-linux-tdesc-features.c
 rename gdbserver/linux-x86-tdesc.h => gdb/arch/x86-linux-tdesc-features.h (52%)
 create mode 100644 gdb/arch/x86-linux-tdesc.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a24b2232daa..1e49ae396f4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -748,6 +748,7 @@ ALL_64_TARGET_OBS = \
 	arch/aarch64-insn.o \
 	arch/aarch64-mte-linux.o \
 	arch/aarch64-scalable-linux.o \
+	arch/amd64-linux-tdesc.o \
 	arch/amd64.o \
 	arch/riscv.o \
 	bpf-tdep.o \
@@ -788,9 +789,11 @@ ALL_TARGET_OBS = \
 	arch/arm.o \
 	arch/arm-get-next-pcs.o \
 	arch/arm-linux.o \
+	arch/i386-linux-tdesc.o \
 	arch/i386.o \
 	arch/loongarch.o \
 	arch/ppc-linux-common.o \
+	arch/x86-linux-tdesc-features.o \
 	arm-bsd-tdep.o \
 	arm-fbsd-tdep.o \
 	arm-linux-tdep.o \
@@ -1558,6 +1561,8 @@ HFILES_NO_SRCDIR = \
 	arch/ppc-linux-common.h \
 	arch/ppc-linux-tdesc.h \
 	arch/riscv.h \
+	arch/x86-linux-tdesc-features.h \
+	arch/x86-linux-tdesc.h \
 	cli/cli-cmds.h \
 	cli/cli-decode.h \
 	cli/cli-script.h \
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index bcb9868e79e..c707745cd9a 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1577,37 +1577,6 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
-const target_desc *
-amd64_linux_read_description (uint64_t xcr0_features_bit, bool is_x32)
-{
-  static target_desc *amd64_linux_tdescs \
-    [2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  static target_desc *x32_linux_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/] = {};
-
-  target_desc **tdesc;
-
-  if (is_x32)
-    {
-      tdesc = &x32_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0 ]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-  else
-    {
-      tdesc = &amd64_linux_tdescs[(xcr0_features_bit & X86_XSTATE_AVX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_MPX) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_AVX512) ? 1 : 0]
-	[(xcr0_features_bit & X86_XSTATE_PKRU) ? 1 : 0];
-    }
-
-  if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0_features_bit, is_x32,
-					      true, true);
-
-  return *tdesc;
-}
-
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
diff --git a/gdb/arch/amd64-linux-tdesc.c b/gdb/arch/amd64-linux-tdesc.c
new file mode 100644
index 00000000000..63b5ddfcece
--- /dev/null
+++ b/gdb/arch/amd64-linux-tdesc.c
@@ -0,0 +1,61 @@
+/* Target description related code for GNU/Linux x86-64.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc.h"
+#include "arch/amd64-linux-tdesc.h"
+#include "arch/amd64.h"
+#include "arch/x86-linux-tdesc-features.h"
+
+
+/* See arch/amd64-linux-tdesc.h.  */
+
+const struct target_desc *
+amd64_linux_read_description (uint64_t xcr0, 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.  */
+  static tdesc_cache_type amd64_tdesc_cache, x32_tdesc_cache;
+
+  tdesc_cache_type &tdesc_cache = is_x32 ? x32_tdesc_cache : amd64_tdesc_cache;
+
+  xcr0 &= is_x32
+    ? x86_linux_x32_tdesc_feature_mask ()
+    : x86_linux_amd64_tdesc_feature_mask ();
+
+  const auto it = tdesc_cache.find (xcr0);
+  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));
+  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));
+  return ptr;
+}
diff --git a/gdb/arch/i386-linux-tdesc.c b/gdb/arch/i386-linux-tdesc.c
new file mode 100644
index 00000000000..6d1f1793457
--- /dev/null
+++ b/gdb/arch/i386-linux-tdesc.c
@@ -0,0 +1,51 @@
+/* Target description related code for GNU/Linux i386.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc.h"
+#include "arch/i386-linux-tdesc.h"
+#include "arch/i386.h"
+#include "arch/x86-linux-tdesc-features.h"
+
+/* See arch/i386-linux-tdesc.h.  */
+
+const target_desc *
+i386_linux_read_description (uint64_t xcr0)
+{
+  /* 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.  */
+  static std::unordered_map<uint64_t, const target_desc_up> i386_tdesc_cache;
+
+  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
+
+  const auto it = i386_tdesc_cache.find (xcr0);
+  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));
+  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));
+  return ptr;
+}
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
new file mode 100644
index 00000000000..91abd9b1928
--- /dev/null
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -0,0 +1,247 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#include "arch/x86-linux-tdesc-features.h"
+
+/* A structure used to describe a single cpu feature that might, or might
+   not, be checked for when creating a target description for one of i386,
+   amd64, or x32.  */
+
+struct x86_tdesc_feature {
+  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
+  uint64_t feature;
+
+  /* Is this feature checked when creating an i386 target description.  */
+  bool is_i386;
+
+  /* Is this feature checked when creating an amd64 target description.  */
+  bool is_amd64;
+
+  /* Is this feature checked when creating an x32 target description.  */
+  bool is_x32;
+};
+
+/* A constant table that describes all of the cpu features that are
+   checked when building a target description for i386, amd64, or x32.  */
+
+static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
+  /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_PKRU,	true,	true, 	true },
+  { X86_XSTATE_AVX512,	true,	true, 	true },
+  { X86_XSTATE_AVX,	true,	true, 	true },
+  { X86_XSTATE_MPX,	true,	true, 	false },
+  { X86_XSTATE_SSE,	true,	false, 	false },
+  { X86_XSTATE_X87,	true,	false, 	false }
+};
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an i386 target description.  */
+
+static constexpr uint64_t
+x86_linux_i386_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an amd64 target description.  */
+
+static constexpr uint64_t
+x86_linux_amd64_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* Return a compile time constant which is a mask of all the cpu features
+   that are checked for when building an x32 target description.  */
+
+static constexpr uint64_t
+x86_linux_x32_tdesc_feature_mask_1 ()
+{
+  uint64_t mask = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      mask |= entry.feature;
+
+  return mask;
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_amd64_tdesc_feature_mask ()
+{
+  return x86_linux_amd64_tdesc_feature_mask_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_x32_tdesc_feature_mask ()
+{
+  return x86_linux_x32_tdesc_feature_mask_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_i386_tdesc_feature_mask ()
+{
+  return x86_linux_i386_tdesc_feature_mask_1 ();
+}
+
+#ifdef GDBSERVER
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+{
+  /* The following table shows which features are checked for when creating
+     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
+     represents the bit order within the generated index number.
+
+     i386  | x87 sse mpx avx avx512 pkru
+     amd64 |         mpx avx avx512 pkru
+     i32   |             avx avx512 pkru
+
+     The features are ordered so that for each mode (i386, amd64, i32) the
+     generated index will form a continuous range.  */
+
+  int idx = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
+	  == x86_linux_all_tdesc_features[i].feature)
+	idx |= (1 << i);
+    }
+
+  return idx;
+}
+
+#endif /* GDBSERVER */
+
+#ifdef IN_PROCESS_AGENT
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an i386 target description.  */
+
+static constexpr int
+x86_linux_i386_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_i386)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an amd64 target description.  */
+
+static constexpr int
+x86_linux_amd64_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_amd64)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* Return a compile time constant which is a count of the number of cpu
+   features that are checked for when building an x32 target description.  */
+
+static constexpr int
+x86_linux_x32_tdesc_count_1 ()
+{
+  uint64_t count = 0;
+
+  for (const auto &entry : x86_linux_all_tdesc_features)
+    if (entry.is_x32)
+      ++count;
+
+  gdb_assert (count > 0);
+
+  return (1 << count);
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_amd64_tdesc_count ()
+{
+  return x86_linux_amd64_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_x32_tdesc_count ()
+{
+  return x86_linux_x32_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+int
+x86_linux_i386_tdesc_count ()
+{
+  return x86_linux_i386_tdesc_count_1 ();
+}
+
+/* See arch/x86-linux-tdesc-features.h.  */
+
+uint64_t
+x86_linux_tdesc_idx_to_xcr0 (int idx)
+{
+  uint64_t xcr0 = 0;
+
+  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
+    {
+      if ((idx & (1 << i)) != 0)
+	xcr0 |= x86_linux_all_tdesc_features[i].feature;
+    }
+
+  return xcr0;
+}
+
+#endif /* IN_PROCESS_AGENT */
diff --git a/gdbserver/linux-x86-tdesc.h b/gdb/arch/x86-linux-tdesc-features.h
similarity index 52%
rename from gdbserver/linux-x86-tdesc.h
rename to gdb/arch/x86-linux-tdesc-features.h
index 3d6e0e51833..0d3db587174 100644
--- a/gdbserver/linux-x86-tdesc.h
+++ b/gdb/arch/x86-linux-tdesc-features.h
@@ -1,7 +1,6 @@
-/* Low level support for x86 (i386 and x86-64), shared between gdbserver
-   and IPA.
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
 
-   Copyright (C) 2016-2024 Free Software Foundation, Inc.
+   Copyright (C) 2024 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -18,33 +17,46 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef GDBSERVER_LINUX_X86_TDESC_H
-#define GDBSERVER_LINUX_X86_TDESC_H
+#ifndef ARCH_X86_LINUX_TDESC_FEATURES_H
+#define ARCH_X86_LINUX_TDESC_FEATURES_H
 
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
+#include "gdbsupport/x86-xstate.h"
+#include "gdbsupport/gdb_assert.h"
 
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+/* Return a mask of X86_STATE_* feature flags.  The returned mask indicates
+   the set of features which are checked for when creating the target
+   description for each of amd64, x32, and i386.  */
 
-#ifdef IN_PROCESS_AGENT
+extern uint64_t x86_linux_amd64_tdesc_feature_mask ();
+extern uint64_t x86_linux_x32_tdesc_feature_mask ();
+extern uint64_t x86_linux_i386_tdesc_feature_mask ();
 
-/* 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.  */
+#ifdef GDBSERVER
 
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+/* Convert an xcr0 value into an integer.  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.  */
+
+extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
 
-/* Within the in-process-agent we need to pre-initialise all of the target
-   descriptions, to do this we need to know how many target descriptions
-   there are for each different target type.  These functions return the
-   target description count for the relevant target.  */
+#endif /* GDBSERVER */
+
+#ifdef IN_PROCESS_AGENT
+
+/* Return the maximum possible number of target descriptions for each of
+   amd64, x32, and i386.  These are used by the in-process-agent to
+   generate every possible target description.  */
 
 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.  */
+
+extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
 
 #endif /* IN_PROCESS_AGENT */
 
-#endif /* GDBSERVER_LINUX_X86_TDESC_H */
+#endif /* ARCH_X86_LINUX_TDESC_FEATURES_H */
diff --git a/gdb/arch/x86-linux-tdesc.h b/gdb/arch/x86-linux-tdesc.h
new file mode 100644
index 00000000000..152592fcf76
--- /dev/null
+++ b/gdb/arch/x86-linux-tdesc.h
@@ -0,0 +1,37 @@
+/* Target description related code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_X86_LINUX_TDESC_H
+#define ARCH_X86_LINUX_TDESC_H
+
+struct target_desc;
+
+/* This function is called from amd64_linux_read_description and
+   i386_linux_read_description after a new target description has been
+   created, TDESC is the new target description, IS_64BIT will be true
+   when called from amd64_linux_read_description, otherwise IS_64BIT will
+   be false.  If the *_linux_read_description functions found a cached
+   target description then this function will not be called.
+
+   Both GDB and gdbserver have their own implementations of this
+   function.  */
+
+extern void x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit);
+
+#endif /* ARCH_X86_LINUX_TDESC_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 4bcc0696027..24e1824b01c 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -256,7 +256,8 @@ case ${gdb_host} in
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
 		nat/x86-xstate.o \
 		i386-linux-nat.o x86-linux-nat.o nat/linux-btrace.o \
-		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o"
+		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
+		arch/i386-linux-tdesc.o arch/x86-linux-tdesc-features.o"
 		;;
 	    ia64)
 		# Host: Intel IA-64 running GNU/Linux
@@ -323,7 +324,8 @@ case ${gdb_host} in
 		nat/x86-xstate.o amd64-nat.o amd64-linux-nat.o x86-linux-nat.o \
 		nat/linux-btrace.o \
 		nat/x86-linux.o nat/x86-linux-dregs.o nat/x86-linux-tdesc.o \
-		nat/amd64-linux-siginfo.o"
+		nat/amd64-linux-siginfo.o arch/x86-linux-tdesc-features.o \
+		arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o"
 		;;
 	    sparc)
 		# Host: GNU/Linux UltraSPARC
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 47a674201f9..8326c458eb1 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -320,10 +320,13 @@ i[34567]86-*-linux*)
 	gdb_target_obs="i386-linux-tdep.o \
 			glibc-tdep.o \
 			solib-svr4.o symfile-mem.o \
-			linux-tdep.o linux-record.o"
+			linux-tdep.o linux-record.o \
+			arch/i386-linux-tdesc.o \
+			arch/x86-linux-tdesc-features.o"
 	if test "x$enable_64_bit_bfd" = "xyes"; then
 	    # Target: GNU/Linux x86-64
-	    gdb_target_obs="amd64-linux-tdep.o ${gdb_target_obs}"
+	    gdb_target_obs="amd64-linux-tdep.o \
+			    arch/amd64-linux-tdesc.o ${gdb_target_obs}"
 	fi
 	;;
 i[34567]86-*-gnu*)
@@ -718,7 +721,9 @@ x86_64-*-linux*)
 	# Target: GNU/Linux x86-64
 	gdb_target_obs="amd64-linux-tdep.o ${i386_tobjs}  \
 			i386-linux-tdep.o glibc-tdep.o \
-			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
+			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o \
+			arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o \
+			arch/x86-linux-tdesc-features.o"
 	;;
 x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
 	# Target: FreeBSD/amd64
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index c796f87780b..d4d820b9f98 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -40,6 +40,7 @@
 #include "i387-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
@@ -679,29 +680,12 @@ i386_linux_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
 					  layout) != 0;
 }
 
-/* See i386-linux-tdep.h.  */
+/* See arch/x86-linux-tdesc.h.  */
 
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  if (xcr0 == 0)
-    return NULL;
-
-  static struct target_desc *i386_linux_tdescs \
-    [2/*X87*/][2/*SSE*/][2/*AVX*/][2/*MPX*/][2/*AVX512*/][2/*PKRU*/] = {};
-  struct target_desc **tdesc;
-
-  tdesc = &i386_linux_tdescs[(xcr0 & X86_XSTATE_X87) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_MPX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0];
-
-  if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, true, false);
-
-  return *tdesc;
+  /* Nothing.  */
 }
 
 /* Get Linux/x86 target description from core dump.  */
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7a2702d78bf..e17b5cf280c 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -110,12 +110,16 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} arch/x86-linux-tdesc-features.o"
+			srv_tgtobj="${srv_tgtobj} arch/i386-linux-tdesc.o"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			srv_linux_btrace=yes
 			ipa_obj="linux-i386-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/i386-ipa.o"
+			ipa_obj="${ipa_obj} arch/x86-linux-tdesc-features-ipa.o"
+			ipa_obj="${ipa_obj} arch/i386-linux-tdesc-ipa.o"
 			;;
   i[34567]86-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
@@ -374,12 +378,17 @@ case "${gdbserver_host}" in
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-dregs.o"
 			srv_tgtobj="${srv_tgtobj} nat/x86-linux-tdesc.o"
 			srv_tgtobj="${srv_tgtobj} nat/amd64-linux-siginfo.o"
+			srv_tgtobj="${srv_tgtobj} arch/x86-linux-tdesc-features.o"
+			srv_tgtobj="${srv_tgtobj} arch/amd64-linux-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} arch/i386-linux-tdesc.o"
 			srv_linux_usrregs=yes # This is for i386 progs.
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			srv_linux_btrace=yes
 			ipa_obj="linux-amd64-ipa.o linux-x86-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/amd64-ipa.o"
+			ipa_obj="${ipa_obj} arch/x86-linux-tdesc-features-ipa.o"
+			ipa_obj="${ipa_obj} arch/amd64-linux-tdesc-ipa.o"
 			;;
   x86_64-*-mingw*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o"
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index 0c80812cc6f..89857f208b8 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -20,9 +20,9 @@
 
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/amd64-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 /* fast tracepoints collect registers.  */
 
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index c1c3152fb04..8100c9f9840 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -20,9 +20,9 @@
 
 #include <sys/mman.h>
 #include "tracepoint.h"
-#include "linux-x86-tdesc.h"
 #include "gdbsupport/x86-xstate.h"
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 /* GDB register numbers.  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 6e23a53118b..b089e818211 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -33,6 +33,7 @@
 #endif
 
 #include "arch/i386-linux-tdesc.h"
+#include "arch/x86-linux-tdesc-features.h"
 
 #include "gdb_proc_service.h"
 /* Don't include elf/common.h if linux/elf.h got included by
@@ -48,7 +49,6 @@
 #include "nat/linux-nat.h"
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
-#include "linux-x86-tdesc.h"
 #include "nat/x86-linux-tdesc.h"
 
 #ifdef __x86_64__
diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc
index 5e12526bf17..13c80762605 100644
--- a/gdbserver/linux-x86-tdesc.cc
+++ b/gdbserver/linux-x86-tdesc.cc
@@ -17,295 +17,19 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+#include "arch/x86-linux-tdesc.h"
 #include "tdesc.h"
-#include "linux-x86-tdesc.h"
-#include "arch/i386.h"
-#include "gdbsupport/x86-xstate.h"
-#ifdef __x86_64__
-#include "arch/amd64.h"
-#include "arch/amd64-linux-tdesc.h"
-#endif
 #include "x86-tdesc.h"
-#include "arch/i386-linux-tdesc.h"
-
-/* A structure used to describe a single cpu feature that might, or might
-   not, be checked for when creating a target description for one of i386,
-   amd64, or x32.  */
-
-struct x86_tdesc_feature {
-  /* The cpu feature mask.  This is a mask against an xcr0 value.  */
-  uint64_t feature;
-
-  /* Is this feature checked when creating an i386 target description.  */
-  bool is_i386;
-
-  /* Is this feature checked when creating an amd64 target description.  */
-  bool is_amd64;
-
-  /* Is this feature checked when creating an x32 target description.  */
-  bool is_x32;
-};
-
-/* A constant table that describes all of the cpu features that are
-   checked when building a target description for i386, amd64, or x32.  */
-
-static constexpr x86_tdesc_feature x86_linux_all_tdesc_features[] = {
-  /* Feature,           i386,	amd64,	x32.  */
-  { X86_XSTATE_PKRU,	true,	true, 	true },
-  { X86_XSTATE_AVX512,	true,	true, 	true },
-  { X86_XSTATE_AVX,	true,	true, 	true },
-  { X86_XSTATE_MPX,	true,	true, 	false },
-  { X86_XSTATE_SSE,	true,	false, 	false },
-  { X86_XSTATE_X87,	true,	false, 	false }
-};
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an i386 target description.  */
-
-static constexpr uint64_t
-x86_linux_i386_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an amd64 target description.  */
-
-static constexpr uint64_t
-x86_linux_amd64_tdesc_feature_mask ()
-{
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      mask |= entry.feature;
-
-  return mask;
-}
 
-/* Return a compile time constant which is a mask of all the cpu features
-   that are checked for when building an x32 target description.  */
+/* See arch/x86-linux-tdesc.h.  */
 
-static constexpr uint64_t
-x86_linux_x32_tdesc_feature_mask ()
+void
+x86_linux_post_init_tdesc (target_desc *tdesc, bool is_64bit)
 {
-  uint64_t mask = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      mask |= entry.feature;
-
-  return mask;
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an i386 target description.  */
-
-static constexpr int
-x86_linux_i386_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_i386)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an amd64 target description.  */
-
-static constexpr int
-x86_linux_amd64_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_amd64)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-/* Return a compile time constant which is a count of the number of cpu
-   features that are checked for when building an x32 target description.  */
-
-static constexpr int
-x86_linux_x32_tdesc_count_1 ()
-{
-  uint64_t count = 0;
-
-  for (const auto &entry : x86_linux_all_tdesc_features)
-    if (entry.is_x32)
-      ++count;
-
-  gdb_assert (count > 0);
-
-  return (1 << count);
-}
-
-#ifdef IN_PROCESS_AGENT
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_amd64_tdesc_count ()
-{
-  return x86_linux_amd64_tdesc_count_1 ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_x32_tdesc_count ()
-{
-  return x86_linux_x32_tdesc_count_1 ();
-}
-
-/* See linux-x86-tdesc.h.  */
-
-int
-x86_linux_i386_tdesc_count ()
-{
-  return x86_linux_i386_tdesc_count_1 ();
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-/* Convert an xcr0 value into an integer.  The integer will be passed to
-   the in-process-agent where it will then be passed to
-   x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value.  */
-
-int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
-{
-  /* The following table shows which features are checked for when creating
-     the target descriptions (see nat/x86-linux-tdesc.c), the feature order
-     represents the bit order within the generated index number.
-
-     i386  | x87 sse mpx avx avx512 pkru
-     amd64 |         mpx avx avx512 pkru
-     i32   |             avx avx512 pkru
-
-     The features are ordered so that for each mode (i386, amd64, i32) the
-     generated index will form a continuous range.  */
-
-  int idx = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-     if ((xcr0 & x86_linux_all_tdesc_features[i].feature)
-	  == x86_linux_all_tdesc_features[i].feature)
-	idx |= (1 << i);
-    }
-
-  return idx;
-}
-
-
-#ifdef IN_PROCESS_AGENT
-
-/* 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.  */
-
-uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
-{
-  uint64_t xcr0 = 0;
-
-  for (int i = 0; i < ARRAY_SIZE (x86_linux_all_tdesc_features); ++i)
-    {
-      if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_tdesc_features[i].feature;
-    }
-
-  return xcr0;
-}
-
-#endif /* IN_PROCESS_AGENT */
-
-#if defined __i386__ || !defined IN_PROCESS_AGENT
-
-/* A cache of all possible i386 target descriptions.  */
-
-static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count_1 ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-i386_linux_read_description (uint64_t xcr0)
-{
-  xcr0 &= x86_linux_i386_tdesc_feature_mask ();
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count_1 ());
-
-  target_desc **tdesc = &i386_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = i386_create_target_description (xcr0, true, false);
-
-      init_target_desc (*tdesc, i386_expedite_regs);
-    }
-
-  return *tdesc;
-}
-#endif
-
 #ifdef __x86_64__
-
-/* A cache of all possible amd64 target descriptions.  */
-
-static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count_1 ()] = { };
-
-/* A cache of all possible x32 target descriptions.  */
-
-static target_desc *x32_tdescs[x86_linux_x32_tdesc_count_1 ()] = { };
-
-/* See nat/x86-linux-tdesc.h.  */
-
-const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
-{
-  if (is_x32)
-    xcr0 &= x86_linux_x32_tdesc_feature_mask ();
+  if (is_64bit)
+    init_target_desc (tdesc, amd64_expedite_regs);
   else
-    xcr0 &= x86_linux_amd64_tdesc_feature_mask ();
-
-  int idx = x86_linux_xcr0_to_tdesc_idx (xcr0);
-
-  if (is_x32)
-    gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count_1 ());
-  else
-    gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count_1 ());
-
-  target_desc **tdesc = nullptr;
-
-  if (is_x32)
-    tdesc = &x32_tdescs[idx];
-  else
-    tdesc = &amd64_tdescs[idx];
-
-  if (*tdesc == nullptr)
-    {
-      *tdesc = amd64_create_target_description (xcr0, is_x32, true, true);
-
-      init_target_desc (*tdesc, amd64_expedite_regs);
-    }
-  return *tdesc;
-}
-
 #endif
+    init_target_desc (tdesc, i386_expedite_regs);
+}
-- 
2.25.4


^ permalink raw reply	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2024-04-26 15:04 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-01 15:28 [PATCH 0/7] x86/Linux Target Description Changes Andrew Burgess
2024-02-01 15:28 ` [PATCH 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
2024-02-01 15:28 ` [PATCH 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
2024-02-01 15:28 ` [PATCH 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
2024-02-01 15:28 ` [PATCH 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
2024-02-01 15:28 ` [PATCH 5/7] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
2024-02-01 15:28 ` [PATCH 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
2024-02-01 15:28 ` [PATCH 7/7] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
2024-03-05 17:00 ` [PATCHv2 0/7] x86/Linux Target Description Changes Andrew Burgess
2024-03-05 17:00   ` [PATCHv2 1/7] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
2024-03-05 17:00   ` [PATCHv2 2/7] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
2024-03-05 17:00   ` [PATCHv2 3/7] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
2024-03-05 17:00   ` [PATCHv2 4/7] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
2024-03-05 17:00   ` [PATCHv2 5/7] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
2024-03-05 17:00   ` [PATCHv2 6/7] gdbserver: update target description creation for x86/linux Andrew Burgess
2024-03-19 16:01     ` John Baldwin
2024-03-19 18:34       ` Andrew Burgess
2024-03-21 17:28         ` John Baldwin
2024-03-26 10:01           ` Luis Machado
2024-03-26 15:31             ` Tom Tromey
2024-03-05 17:00   ` [PATCHv2 7/7] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
2024-03-19 16:05   ` [PATCHv2 0/7] x86/Linux Target Description Changes John Baldwin
2024-03-23 16:35   ` [PATCHv3 0/8] " Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 1/8] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 2/8] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 3/8] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 4/8] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 5/8] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 6/8] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 7/8] gdbserver: update target description creation for x86/linux Andrew Burgess
2024-03-23 16:35     ` [PATCHv3 8/8] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
2024-03-26 12:17       ` Andrew Burgess
2024-03-25 17:20     ` [PATCHv3 0/8] x86/Linux Target Description Changes Andrew Burgess
2024-03-25 18:26       ` Simon Marchi
2024-03-26 12:15         ` Andrew Burgess
2024-03-26 13:51           ` H.J. Lu
2024-03-26 14:16             ` H.J. Lu
2024-03-26 16:36       ` Andrew Burgess
2024-03-26 19:03         ` Andrew Burgess
2024-04-05 12:33     ` [PATCHv4 00/10] " Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 01/10] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 02/10] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 03/10] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 04/10] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 05/10] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 06/10] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 07/10] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 08/10] gdbserver: update target description creation for x86/linux Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 09/10] gdb: move xcr0 == 0 check into i386_linux_core_read_description Andrew Burgess
2024-04-05 12:33       ` [PATCHv4 10/10] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess
2024-04-09 18:37       ` [PATCHv4 00/10] x86/Linux Target Description Changes John Baldwin
2024-04-25 13:35       ` Willgerodt, Felix
2024-04-25 16:06         ` Andrew Burgess
2024-04-26 15:01       ` [PATCHv5 00/11] " Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 01/11] gdbserver/ipa/x86: remove unneeded declarations Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 02/11] gdbserver: convert have_ptrace_getregset to a tribool Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 03/11] gdb/x86: move reading of cs and ds state into gdb/nat directory Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 04/11] gdb/x86: move have_ptrace_getfpxregs global " Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 05/11] gdbserver/x86: move no-xml code earlier in x86_linux_read_description Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 06/11] gdb/gdbserver: share I386_LINUX_XSAVE_XCR0_OFFSET definition Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 07/11] gdb/gdbserver: share some code relating to target description creation Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 08/11] gdb/arch: assert that X86_XSTATE_MPX is not set for x32 Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 09/11] gdbserver: update target description creation for x86/linux Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 10/11] gdb: move xcr0 == 0 check into i386_linux_core_read_description Andrew Burgess
2024-04-26 15:01         ` [PATCHv5 11/11] gdb/gdbserver: share x86/linux tdesc caching Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox