diff --git a/gdb/corelow.c b/gdb/corelow.c
index 46bb1077b6d..3d666a6d311 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.  */
@@ -1387,6 +1390,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 101b1b73636..d62eefa1c5b 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 3d97919bc9b..a9d6a4b175c 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 23e5789327c..846467b8d83 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,
