[v5,03/19] core: Support fetching x86 XSAVE layout from architectures.

Message ID 20230427210113.45380-4-jhb@FreeBSD.org
State New
Headers
Series Handle variable XSAVE layouts |

Commit Message

John Baldwin April 27, 2023, 9 p.m. UTC
  Add gdbarch_core_read_x86_xsave_layout to fetch the x86 XSAVE layout
structure from a core file.

Current OS's do not export the offsets of XSAVE state components in
core dumps, so provide an i387_set_xsave_layout helper function to set
offsets based on known combinations of XCR0 masks and total state
sizes.  Eventually when core dumps do contain this information this
function should only be used as a fall back for older core dumps.
---
 gdb/corelow.c             | 21 +++++++++++++++
 gdb/gdbarch-gen.h         |  9 +++++++
 gdb/gdbarch.c             | 32 +++++++++++++++++++++++
 gdb/gdbarch.h             |  1 +
 gdb/gdbarch_components.py | 11 ++++++++
 gdb/i387-tdep.c           | 55 +++++++++++++++++++++++++++++++++++++++
 gdb/i387-tdep.h           |  8 ++++++
 7 files changed, 137 insertions(+)
  

Patch

diff --git a/gdb/corelow.c b/gdb/corelow.c
index db489b4280e..58982b2dfc7 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -47,6 +47,7 @@ 
 #include "build-id.h"
 #include "gdbsupport/pathstuff.h"
 #include "gdbsupport/scoped_fd.h"
+#include "gdbsupport/x86-xstate.h"
 #include "debuginfod-support.h"
 #include <unordered_map>
 #include <unordered_set>
@@ -109,6 +110,8 @@  class core_target final : public process_stratum_target
   bool fetch_memtags (CORE_ADDR address, size_t len,
 		      gdb::byte_vector &tags, int type) override;
 
+  x86_xsave_layout fetch_x86_xsave_layout () override;
+
   /* A few helpers.  */
 
   /* Getter, see variable definition.  */
@@ -1239,6 +1242,24 @@  core_target::fetch_memtags (CORE_ADDR address, size_t len,
   return false;
 }
 
+/* Implementation of the "fetch_x86_xsave_layout" target_ops method.  */
+
+x86_xsave_layout
+core_target::fetch_x86_xsave_layout ()
+{
+  if (m_core_gdbarch != nullptr &&
+      gdbarch_core_read_x86_xsave_layout_p (m_core_gdbarch))
+    {
+      x86_xsave_layout layout;
+      if (!gdbarch_core_read_x86_xsave_layout (m_core_gdbarch, layout))
+	return {};
+
+      return layout;
+    }
+
+  return {};
+}
+
 /* Get a pointer to the current core target.  If not connected to a
    core target, return NULL.  */
 
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 7f83bf4e214..082e7d77b3d 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1013,6 +1013,15 @@  typedef LONGEST (gdbarch_core_xfer_siginfo_ftype) (struct gdbarch *gdbarch, gdb_
 extern LONGEST gdbarch_core_xfer_siginfo (struct gdbarch *gdbarch, gdb_byte *readbuf, ULONGEST offset, ULONGEST len);
 extern void set_gdbarch_core_xfer_siginfo (struct gdbarch *gdbarch, gdbarch_core_xfer_siginfo_ftype *core_xfer_siginfo);
 
+/* Read x86 XSAVE layout information from core file into XSAVE_LAYOUT.
+   Returns true if the layout was read successfully. */
+
+extern bool gdbarch_core_read_x86_xsave_layout_p (struct gdbarch *gdbarch);
+
+typedef bool (gdbarch_core_read_x86_xsave_layout_ftype) (struct gdbarch *gdbarch, x86_xsave_layout &xsave_layout);
+extern bool gdbarch_core_read_x86_xsave_layout (struct gdbarch *gdbarch, x86_xsave_layout &xsave_layout);
+extern void set_gdbarch_core_read_x86_xsave_layout (struct gdbarch *gdbarch, gdbarch_core_read_x86_xsave_layout_ftype *core_read_x86_xsave_layout);
+
 /* BFD target to use when generating a core file. */
 
 extern bool gdbarch_gcore_bfd_target_p (struct gdbarch *gdbarch);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 30199a0b03d..1fc254d3d6e 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -182,6 +182,7 @@  struct gdbarch
   gdbarch_core_pid_to_str_ftype *core_pid_to_str = nullptr;
   gdbarch_core_thread_name_ftype *core_thread_name = nullptr;
   gdbarch_core_xfer_siginfo_ftype *core_xfer_siginfo = nullptr;
+  gdbarch_core_read_x86_xsave_layout_ftype *core_read_x86_xsave_layout = nullptr;
   const char * gcore_bfd_target = 0;
   int vtable_function_descriptors = 0;
   int vbit_in_delta = 0;
@@ -443,6 +444,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of core_pid_to_str, has predicate.  */
   /* Skip verify of core_thread_name, has predicate.  */
   /* Skip verify of core_xfer_siginfo, has predicate.  */
+  /* Skip verify of core_read_x86_xsave_layout, has predicate.  */
   /* Skip verify of gcore_bfd_target, has predicate.  */
   /* Skip verify of vtable_function_descriptors, invalid_p == 0 */
   /* Skip verify of vbit_in_delta, invalid_p == 0 */
@@ -1071,6 +1073,12 @@  gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: core_xfer_siginfo = <%s>\n",
 	      host_address_to_string (gdbarch->core_xfer_siginfo));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_core_read_x86_xsave_layout_p() = %d\n",
+	      gdbarch_core_read_x86_xsave_layout_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: core_read_x86_xsave_layout = <%s>\n",
+	      host_address_to_string (gdbarch->core_read_x86_xsave_layout));
   gdb_printf (file,
 	      "gdbarch_dump: gdbarch_gcore_bfd_target_p() = %d\n",
 	      gdbarch_gcore_bfd_target_p (gdbarch));
@@ -3958,6 +3966,30 @@  set_gdbarch_core_xfer_siginfo (struct gdbarch *gdbarch,
   gdbarch->core_xfer_siginfo = core_xfer_siginfo;
 }
 
+bool
+gdbarch_core_read_x86_xsave_layout_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->core_read_x86_xsave_layout != NULL;
+}
+
+bool
+gdbarch_core_read_x86_xsave_layout (struct gdbarch *gdbarch, x86_xsave_layout &xsave_layout)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->core_read_x86_xsave_layout != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_core_read_x86_xsave_layout called\n");
+  return gdbarch->core_read_x86_xsave_layout (gdbarch, xsave_layout);
+}
+
+void
+set_gdbarch_core_read_x86_xsave_layout (struct gdbarch *gdbarch,
+					gdbarch_core_read_x86_xsave_layout_ftype core_read_x86_xsave_layout)
+{
+  gdbarch->core_read_x86_xsave_layout = core_read_x86_xsave_layout;
+}
+
 bool
 gdbarch_gcore_bfd_target_p (struct gdbarch *gdbarch)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index f0399c2fa88..81f7dfa5fd8 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -57,6 +57,7 @@  struct syscalls_info;
 struct thread_info;
 struct ui_out;
 struct inferior;
+struct x86_xsave_layout;
 
 #include "regcache.h"
 
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 07010b13311..20bf2df1d19 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -1709,6 +1709,17 @@  of bytes read (zero indicates EOF, a negative value indicates failure).
     predicate=True,
 )
 
+Method(
+    comment="""
+Read x86 XSAVE layout information from core file into XSAVE_LAYOUT.
+Returns true if the layout was read successfully.
+""",
+    type="bool",
+    name="core_read_x86_xsave_layout",
+    params=[("x86_xsave_layout &", "xsave_layout")],
+    predicate=True,
+)
+
 Value(
     comment="""
 BFD target to use when generating a core file.
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index df0a6058adc..f25579c1a9a 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -898,6 +898,61 @@  static int xsave_pkeys_offset[] =
   (xsave + xsave_pkeys_offset[regnum - I387_PKRU_REGNUM (tdep)])
 
 
+/* See i387-tdep.h.  */
+
+bool
+i387_set_xsave_layout (uint64_t xcr0, size_t xsave_size,
+		       x86_xsave_layout &layout)
+{
+  if (HAS_PKRU (xcr0) && xsave_size == 2696)
+    {
+      /* Intel CPUs supporting PKRU.  */
+      layout.avx_offset = 576;
+      layout.bndregs_offset = 960;
+      layout.bndcfg_offset = 1024;
+      layout.k_offset = 1088;
+      layout.zmm_h_offset = 1152;
+      layout.zmm_offset = 1664;
+      layout.pkru_offset = 2688;
+    }
+  else if (HAS_PKRU (xcr0) && xsave_size == 2440)
+    {
+      /* AMD CPUs supporting PKRU.  */
+      layout.avx_offset = 576;
+      layout.k_offset = 832;
+      layout.zmm_h_offset = 896;
+      layout.zmm_offset = 1408;
+      layout.pkru_offset = 2432;
+    }
+  else if (HAS_AVX512 (xcr0) && xsave_size == 2688)
+    {
+      /* Intel CPUs supporting AVX512.  */
+      layout.avx_offset = 576;
+      layout.bndregs_offset = 960;
+      layout.bndcfg_offset = 1024;
+      layout.k_offset = 1088;
+      layout.zmm_h_offset = 1152;
+      layout.zmm_offset = 1664;
+    }
+  else if (HAS_MPX (xcr0) && xsave_size == 1088)
+    {
+      /* Intel CPUs supporting MPX.  */
+      layout.avx_offset = 576;
+      layout.bndregs_offset = 960;
+      layout.bndcfg_offset = 1024;
+    }
+  else if (HAS_AVX (xcr0) && xsave_size == 832)
+    {
+      /* Intel and AMD CPUs supporting AVX.  */
+      layout.avx_offset = 576;
+    }
+  else
+    return false;
+
+  layout.sizeof_xsave = xsave_size;
+  return true;
+}
+
 /* Extract from XSAVE a bitset of the features that are available on the
    target, but which have not yet been enabled.  */
 
diff --git a/gdb/i387-tdep.h b/gdb/i387-tdep.h
index 2e890e71a2b..b7674155255 100644
--- a/gdb/i387-tdep.h
+++ b/gdb/i387-tdep.h
@@ -25,6 +25,7 @@  class frame_info_ptr;
 struct regcache;
 struct type;
 struct ui_file;
+struct x86_xsave_layout;
 
 /* Number of i387 floating point registers.  */
 #define I387_NUM_REGS	16
@@ -138,6 +139,13 @@  extern void i387_collect_fsave (const struct regcache *regcache, int regnum,
 extern void i387_supply_fxsave (struct regcache *regcache, int regnum,
 				const void *fxsave);
 
+/* Select an XSAVE layout based on the XCR0 bitmask and total XSAVE
+   extended state size.  Returns true if the bitmask and size matched
+   a known layout.  */
+
+extern bool i387_set_xsave_layout (uint64_t xcr0, size_t xsave_size,
+				   x86_xsave_layout &layout);
+
 /* Similar to i387_supply_fxsave, but use XSAVE extended state.  */
 
 extern void i387_supply_xsave (struct regcache *regcache, int regnum,