[8/8] FreeBSD x86 nat: Use register maps for GP register sets.

Message ID 20210714140741.6460-9-jhb@FreeBSD.org
State New
Headers show
Series
  • Switch FreeBSD x86 to using register maps for GP registers
Related show

Commit Message

John Baldwin July 14, 2021, 2:07 p.m.
Rather than using the x86-specific register offset tables, use
register maps to describe the layout of the general purpose registers
fetched via PT_GETREGS.  The sole user-visible difference is that
FreeBSD/amd64 will now report additional segment registers ($ds, $es,
$fs, and $gs) for both 32-bit and 64-bit processes.

As part of these changes, the FreeBSD x86 native targets no longer use
amd64-bsd-nat.c or i386-bsd-nat.c.  Remove FreeBSD-specific register
handling (for $fs_base, $gs_base, and XSAVE state) from these files.
Similarly, remove the global x86bsd_xsave_len from x86-bsd-nat.c.  The
FreeBSD x86 native targets use a static xsave_len instead.

While here, rework the probing of PT_GETXMMREGS on FreeBSD/i386.
Probe the ptrace op once in the target read_description method and
cache the result for the future similar to the way the status of XSAVE
support is probed in the read_description method.  In addition, return
the proper xcr0 mask (X87-only) for old kernels or systems without
either XSAVE or XMM support.
---
 gdb/amd64-bsd-nat.c   |  96 ----------------
 gdb/amd64-fbsd-nat.c  | 260 +++++++++++++++++++++++++++++++++---------
 gdb/amd64-fbsd-tdep.c |   1 +
 gdb/amd64-fbsd-tdep.h |  27 +++++
 gdb/configure.nat     |   4 +-
 gdb/i386-bsd-nat.c    |  92 ---------------
 gdb/i386-fbsd-nat.c   | 229 +++++++++++++++++++++++++++++++++++--
 gdb/i386-fbsd-tdep.h  |   4 +
 gdb/x86-bsd-nat.c     |   4 -
 gdb/x86-bsd-nat.h     |   3 -
 10 files changed, 457 insertions(+), 263 deletions(-)
 create mode 100644 gdb/amd64-fbsd-tdep.h

-- 
2.31.1

Patch

diff --git a/gdb/amd64-bsd-nat.c b/gdb/amd64-bsd-nat.c
index 3e0c29a22f..e994bb71b6 100644
--- a/gdb/amd64-bsd-nat.c
+++ b/gdb/amd64-bsd-nat.c
@@ -59,9 +59,6 @@  amd64bsd_fetch_inferior_registers (struct regcache *regcache, int regnum)
 {
   struct gdbarch *gdbarch = regcache->arch ();
   ptid_t ptid = regcache->ptid ();
-#if defined(PT_GETFSBASE) || defined(PT_GETGSBASE)
-  const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-#endif
 
   if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
@@ -75,50 +72,9 @@  amd64bsd_fetch_inferior_registers (struct regcache *regcache, int regnum)
 	return;
     }
 
-#ifdef PT_GETFSBASE
-  if (regnum == -1 || regnum == tdep->fsbase_regnum)
-    {
-      register_t base;
-
-      if (gdb_ptrace (PT_GETFSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't get segment register fs_base"));
-
-      regcache->raw_supply (tdep->fsbase_regnum, &base);
-      if (regnum != -1)
-	return;
-    }
-#endif
-#ifdef PT_GETGSBASE
-  if (regnum == -1 || regnum == tdep->fsbase_regnum + 1)
-    {
-      register_t base;
-
-      if (gdb_ptrace (PT_GETGSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't get segment register gs_base"));
-
-      regcache->raw_supply (tdep->fsbase_regnum + 1, &base);
-      if (regnum != -1)
-	return;
-    }
-#endif
-
   if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       struct fpreg fpregs;
-#ifdef PT_GETXSTATE_INFO
-      void *xstateregs;
-
-      if (x86bsd_xsave_len != 0)
-	{
-	  xstateregs = alloca (x86bsd_xsave_len);
-	  if (gdb_ptrace (PT_GETXSTATE, ptid, (PTRACE_TYPE_ARG3) xstateregs, 0)
-	      == -1)
-	    perror_with_name (_("Couldn't get extended state status"));
-
-	  amd64_supply_xsave (regcache, -1, xstateregs);
-	  return;
-	}
-#endif
 
       if (gdb_ptrace (PT_GETFPREGS, ptid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
@@ -135,9 +91,6 @@  amd64bsd_store_inferior_registers (struct regcache *regcache, int regnum)
 {
   struct gdbarch *gdbarch = regcache->arch ();
   ptid_t ptid = regcache->ptid ();
-#if defined(PT_SETFSBASE) || defined(PT_SETGSBASE)
-  const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-#endif
 
   if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
@@ -155,58 +108,9 @@  amd64bsd_store_inferior_registers (struct regcache *regcache, int regnum)
 	return;
     }
 
-#ifdef PT_SETFSBASE
-  if (regnum == -1 || regnum == tdep->fsbase_regnum)
-    {
-      register_t base;
-
-      /* Clear the full base value to support 32-bit targets.  */
-      base = 0;
-      regcache->raw_collect (tdep->fsbase_regnum, &base);
-
-      if (gdb_ptrace (PT_SETFSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't write segment register fs_base"));
-      if (regnum != -1)
-	return;
-    }
-#endif
-#ifdef PT_SETGSBASE
-  if (regnum == -1 || regnum == tdep->fsbase_regnum + 1)
-    {
-      register_t base;
-
-      /* Clear the full base value to support 32-bit targets.  */
-      base = 0;
-      regcache->raw_collect (tdep->fsbase_regnum + 1, &base);
-
-      if (gdb_ptrace (PT_SETGSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't write segment register gs_base"));
-      if (regnum != -1)
-	return;
-    }
-#endif
-
   if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       struct fpreg fpregs;
-#ifdef PT_GETXSTATE_INFO
-      void *xstateregs;
-
-      if (x86bsd_xsave_len != 0)
-	{
-	  xstateregs = alloca (x86bsd_xsave_len);
-	  if (gdb_ptrace (PT_GETXSTATE, ptid, (PTRACE_TYPE_ARG3) xstateregs, 0)
-	      == -1)
-	    perror_with_name (_("Couldn't get extended state status"));
-
-	  amd64_collect_xsave (regcache, regnum, xstateregs, 0);
-
-	  if (gdb_ptrace (PT_SETXSTATE, ptid, (PTRACE_TYPE_ARG3) xstateregs,
-			  x86bsd_xsave_len) == -1)
-	    perror_with_name (_("Couldn't write extended state status"));
-	  return;
-	}
-#endif
 
       if (gdb_ptrace (PT_GETFPREGS, ptid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c
index 91fc477d4d..a20d3c04b8 100644
--- a/gdb/amd64-fbsd-nat.c
+++ b/gdb/amd64-fbsd-nat.c
@@ -31,17 +31,19 @@ 
 
 #include "fbsd-nat.h"
 #include "amd64-tdep.h"
+#include "amd64-fbsd-tdep.h"
 #include "amd64-nat.h"
-#include "amd64-bsd-nat.h"
 #include "x86-nat.h"
 #include "gdbsupport/x86-xstate.h"
-
+#include "x86-bsd-nat.h"
 
 class amd64_fbsd_nat_target final
-  : public amd64_bsd_nat_target<fbsd_nat_target>
+  : public x86bsd_nat_target<fbsd_nat_target>
 {
 public:
-  /* Add some extra features to the common *BSD/amd64 target.  */
+  void fetch_registers (struct regcache *, int) override;
+  void store_registers (struct regcache *, int) override;
+
   const struct target_desc *read_description () override;
 
 #if defined(HAVE_PT_GETDBREGS) && defined(USE_SIGTRAP_SIGINFO)
@@ -51,61 +53,208 @@  class amd64_fbsd_nat_target final
 
 static amd64_fbsd_nat_target the_amd64_fbsd_nat_target;
 
-/* Offset in `struct reg' where MEMBER is stored.  */
-#define REG_OFFSET(member) offsetof (struct reg, member)
+#ifdef PT_GETXSTATE_INFO
+static size_t xsave_len;
+#endif
 
-/* At amd64fbsd64_r_reg_offset[REGNUM] you'll find the offset in
-   `struct reg' location where the GDB register REGNUM is stored.
-   Unsupported registers are marked with `-1'.  */
-static int amd64fbsd64_r_reg_offset[] =
+/* This is a layout of the amd64 'struct reg' but with i386
+   registers.  */
+
+static const struct regcache_map_entry amd64_fbsd32_gregmap[] =
 {
-  REG_OFFSET (r_rax),
-  REG_OFFSET (r_rbx),
-  REG_OFFSET (r_rcx),
-  REG_OFFSET (r_rdx),
-  REG_OFFSET (r_rsi),
-  REG_OFFSET (r_rdi),
-  REG_OFFSET (r_rbp),
-  REG_OFFSET (r_rsp),
-  REG_OFFSET (r_r8),
-  REG_OFFSET (r_r9),
-  REG_OFFSET (r_r10),
-  REG_OFFSET (r_r11),
-  REG_OFFSET (r_r12),
-  REG_OFFSET (r_r13),
-  REG_OFFSET (r_r14),
-  REG_OFFSET (r_r15),
-  REG_OFFSET (r_rip),
-  REG_OFFSET (r_rflags),
-  REG_OFFSET (r_cs),
-  REG_OFFSET (r_ss),
-  -1,
-  -1,
-  -1,
-  -1
+  { 8, REGCACHE_MAP_SKIP, 8 },
+  { 1, I386_EDI_REGNUM, 8 },
+  { 1, I386_ESI_REGNUM, 8 },
+  { 1, I386_EBP_REGNUM, 8 },
+  { 1, I386_EBX_REGNUM, 8 },
+  { 1, I386_EDX_REGNUM, 8 },
+  { 1, I386_ECX_REGNUM, 8 },
+  { 1, I386_EAX_REGNUM, 8 },
+  { 1, REGCACHE_MAP_SKIP, 4 },	/* trapno */
+  { 1, I386_FS_REGNUM, 2 },
+  { 1, I386_GS_REGNUM, 2 },
+  { 1, REGCACHE_MAP_SKIP, 4 },	/* err */
+  { 1, I386_ES_REGNUM, 2 },
+  { 1, I386_DS_REGNUM, 2 },
+  { 1, I386_EIP_REGNUM, 8 },
+  { 1, I386_CS_REGNUM, 8 },
+  { 1, I386_EFLAGS_REGNUM, 8 },
+  { 1, I386_ESP_REGNUM, 0 },
+  { 1, I386_SS_REGNUM, 8 },
+  { 0 }
 };
-
 
-/* Mapping between the general-purpose registers in FreeBSD/amd64
-   `struct reg' format and GDB's register cache layout for
-   FreeBSD/i386.
+static const struct regset amd64_fbsd32_gregset =
+{
+  amd64_fbsd32_gregmap, regcache_supply_regset, regcache_collect_regset
+};
 
-   Note that most FreeBSD/amd64 registers are 64-bit, while the
-   FreeBSD/i386 registers are all 32-bit, but since we're
-   little-endian we get away with that.  */
+/* Return the regset to use for 'struct reg' for the GDBARCH.  */
 
-/* From <machine/reg.h>.  */
-static int amd64fbsd32_r_reg_offset[I386_NUM_GREGS] =
+static const struct regset *
+find_gregset (struct gdbarch *gdbarch)
 {
-  14 * 8, 13 * 8,		/* %eax, %ecx */
-  12 * 8, 11 * 8,		/* %edx, %ebx */
-  20 * 8, 10 * 8,		/* %esp, %ebp */
-  9 * 8, 8 * 8,			/* %esi, %edi */
-  17 * 8, 19 * 8,		/* %eip, %eflags */
-  18 * 8, 21 * 8,		/* %cs, %ss */
-  -1, -1, -1, -1		/* %ds, %es, %fs, %gs */
-};
-
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    return &amd64_fbsd32_gregset;
+  else
+    return &amd64_fbsd_gregset;
+}
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers.  */
+
+void
+amd64_fbsd_nat_target::fetch_registers (struct regcache *regcache, int regnum)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+#if defined(PT_GETFSBASE) || defined(PT_GETGSBASE)
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+#endif
+  pid_t pid = get_ptrace_pid (regcache->ptid ());
+  const struct regset *gregset = find_gregset (gdbarch);
+
+  if (fetch_register_set<struct reg> (regcache, regnum, PT_GETREGS, gregset))
+    {
+      if (regnum != -1)
+	return;
+    }
+
+#ifdef PT_GETFSBASE
+  if (regnum == -1 || regnum == tdep->fsbase_regnum)
+    {
+      register_t base;
+
+      if (ptrace (PT_GETFSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't get segment register fs_base"));
+
+      regcache->raw_supply (tdep->fsbase_regnum, &base);
+      if (regnum != -1)
+	return;
+    }
+#endif
+#ifdef PT_GETGSBASE
+  if (regnum == -1 || regnum == tdep->fsbase_regnum + 1)
+    {
+      register_t base;
+
+      if (ptrace (PT_GETGSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't get segment register gs_base"));
+
+      regcache->raw_supply (tdep->fsbase_regnum + 1, &base);
+      if (regnum != -1)
+	return;
+    }
+#endif
+
+  /* There is no amd64_fxsave_supplies or amd64_xsave_supplies.
+     Instead, the earlier register sets return early if the request
+     was for a specific register that was already satisified to avoid
+     fetching the FPU/XSAVE state unnecessarily.  */
+
+#ifdef PT_GETXSTATE_INFO
+  if (xsave_len != 0)
+    {
+      void *xstateregs = alloca (xsave_len);
+
+      if (ptrace (PT_GETXSTATE, pid, (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+	perror_with_name (_("Couldn't get extended state status"));
+
+      amd64_supply_xsave (regcache, regnum, xstateregs);
+      return;
+    }
+#endif
+
+  struct fpreg fpregs;
+
+  if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+    perror_with_name (_("Couldn't get floating point status"));
+
+  amd64_supply_fxsave (regcache, regnum, &fpregs);
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers.  */
+
+void
+amd64_fbsd_nat_target::store_registers (struct regcache *regcache, int regnum)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+#if defined(PT_GETFSBASE) || defined(PT_GETGSBASE)
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+#endif
+  pid_t pid = get_ptrace_pid (regcache->ptid ());
+  const struct regset *gregset = find_gregset (gdbarch);
+
+  if (store_register_set<struct reg> (regcache, regnum, PT_GETREGS, PT_SETREGS,
+				      gregset))
+    {
+      if (regnum != -1)
+	return;
+    }
+
+#ifdef PT_SETFSBASE
+  if (regnum == -1 || regnum == tdep->fsbase_regnum)
+    {
+      register_t base;
+
+      /* Clear the full base value to support 32-bit targets.  */
+      base = 0;
+      regcache->raw_collect (tdep->fsbase_regnum, &base);
+
+      if (ptrace (PT_SETFSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't write segment register fs_base"));
+      if (regnum != -1)
+	return;
+    }
+#endif
+#ifdef PT_SETGSBASE
+  if (regnum == -1 || regnum == tdep->fsbase_regnum + 1)
+    {
+      register_t base;
+
+      /* Clear the full base value to support 32-bit targets.  */
+      base = 0;
+      regcache->raw_collect (tdep->fsbase_regnum + 1, &base);
+
+      if (ptrace (PT_SETGSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't write segment register gs_base"));
+      if (regnum != -1)
+	return;
+    }
+#endif
+
+  /* There is no amd64_fxsave_supplies or amd64_xsave_supplies.
+     Instead, the earlier register sets return early if the request
+     was for a specific register that was already satisified to avoid
+     fetching the FPU/XSAVE state unnecessarily.  */
+
+#ifdef PT_GETXSTATE_INFO
+  if (xsave_len != 0)
+    {
+      void *xstateregs = alloca (xsave_len);
+
+      if (ptrace (PT_GETXSTATE, pid, (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+	perror_with_name (_("Couldn't get extended state status"));
+
+      amd64_collect_xsave (regcache, regnum, xstateregs, 0);
+
+      if (ptrace (PT_SETXSTATE, pid, (PTRACE_TYPE_ARG3) xstateregs,
+		  xsave_len) == -1)
+	perror_with_name (_("Couldn't write extended state status"));
+      return;
+    }
+#endif
+
+  struct fpreg fpregs;
+
+  if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+    perror_with_name (_("Couldn't get floating point status"));
+
+  amd64_collect_fxsave (regcache, regnum, &fpregs);
+
+  if (ptrace (PT_SETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+    perror_with_name (_("Couldn't write floating point status"));
+}
 
 /* Support for debugging kernel virtual memory images.  */
 
@@ -179,13 +328,13 @@  amd64_fbsd_nat_target::read_description ()
       if (ptrace (PT_GETXSTATE_INFO, inferior_ptid.pid (),
 		  (PTRACE_TYPE_ARG3) &info, sizeof (info)) == 0)
 	{
-	  x86bsd_xsave_len = info.xsave_len;
+	  xsave_len = info.xsave_len;
 	  xcr0 = info.xsave_mask;
 	}
       xsave_probed = 1;
     }
 
-  if (x86bsd_xsave_len != 0)
+  if (xsave_len != 0)
     {
       if (is64)
 	return amd64_target_description (xcr0, true);
@@ -213,9 +362,6 @@  void _initialize_amd64fbsd_nat ();
 void
 _initialize_amd64fbsd_nat ()
 {
-  amd64_native_gregset32_reg_offset = amd64fbsd32_r_reg_offset;
-  amd64_native_gregset64_reg_offset = amd64fbsd64_r_reg_offset;
-
   add_inf_child_target (&the_amd64_fbsd_nat_target);
 
   /* Support debugging kernel virtual memory images.  */
diff --git a/gdb/amd64-fbsd-tdep.c b/gdb/amd64-fbsd-tdep.c
index 648f929546..859a2bff7a 100644
--- a/gdb/amd64-fbsd-tdep.c
+++ b/gdb/amd64-fbsd-tdep.c
@@ -27,6 +27,7 @@ 
 #include "gdbsupport/x86-xstate.h"
 
 #include "amd64-tdep.h"
+#include "amd64-fbsd-tdep.h"
 #include "fbsd-tdep.h"
 #include "solib-svr4.h"
 #include "inferior.h"
diff --git a/gdb/amd64-fbsd-tdep.h b/gdb/amd64-fbsd-tdep.h
new file mode 100644
index 0000000000..0a18dbcbfd
--- /dev/null
+++ b/gdb/amd64-fbsd-tdep.h
@@ -0,0 +1,27 @@ 
+/* FreeBSD/amd64 target support, prototypes.
+
+   Copyright (C) 2021 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 AMD64_FBSD_TDEP_H
+#define AMD64_FBSD_TDEP_H
+
+#include "regset.h"
+
+extern const struct regset amd64_fbsd_gregset;
+
+#endif /* AMD64_FBSD_TDEP_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index e34cccffd9..26f214cd87 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -165,7 +165,7 @@  case ${gdb_host} in
 	    i386)
 		# Host: FreeBSD/i386
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
-		x86-bsd-nat.o i386-bsd-nat.o i386-fbsd-nat.o bsd-kvm.o"
+		x86-bsd-nat.o i386-fbsd-nat.o bsd-kvm.o"
 		;;
 	    mips)
 		# Host: FreeBSD/mips
@@ -192,7 +192,7 @@  case ${gdb_host} in
 	case ${gdb_host_cpu} in
 	    i386)
 		# Host: FreeBSD/amd64
-		NATDEPFILES="${NATDEPFILES} amd64-nat.o amd64-bsd-nat.o \
+		NATDEPFILES="${NATDEPFILES} amd64-nat.o \
 		amd64-fbsd-nat.o bsd-kvm.o x86-nat.o nat/x86-dregs.o \
 		x86-bsd-nat.o"
 		;;
diff --git a/gdb/i386-bsd-nat.c b/gdb/i386-bsd-nat.c
index 7312c4bc12..6006dadf7e 100644
--- a/gdb/i386-bsd-nat.c
+++ b/gdb/i386-bsd-nat.c
@@ -159,56 +159,12 @@  i386bsd_fetch_inferior_registers (struct regcache *regcache, int regnum)
 	return;
     }
 
-#ifdef PT_GETFSBASE
-  if (regnum == -1 || regnum == I386_FSBASE_REGNUM)
-    {
-      register_t base;
-
-      if (gdb_ptrace (PT_GETFSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't get segment register fs_base"));
-
-      regcache->raw_supply (I386_FSBASE_REGNUM, &base);
-      if (regnum != -1)
-	return;
-    }
-#endif
-#ifdef PT_GETGSBASE
-  if (regnum == -1 || regnum == I386_GSBASE_REGNUM)
-    {
-      register_t base;
-
-      if (gdb_ptrace (PT_GETGSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't get segment register gs_base"));
-
-      regcache->raw_supply (I386_GSBASE_REGNUM, &base);
-      if (regnum != -1)
-	return;
-    }
-#endif
-
   if (regnum == -1 || regnum >= I386_ST0_REGNUM)
     {
       struct fpreg fpregs;
 #ifdef HAVE_PT_GETXMMREGS
       char xmmregs[512];
-#endif
 
-#ifdef PT_GETXSTATE_INFO
-      if (x86bsd_xsave_len != 0)
-	{
-	  void *xstateregs;
-
-	  xstateregs = alloca (x86bsd_xsave_len);
-	  if (gdb_ptrace (PT_GETXSTATE, ptid,
-			  (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
-	    perror_with_name (_("Couldn't get extended state status"));
-
-	  i387_supply_xsave (regcache, -1, xstateregs);
-	  return;
-	}
-#endif
-      
-#ifdef HAVE_PT_GETXMMREGS
       if (have_ptrace_xmmregs != 0
 	  && gdb_ptrace(PT_GETXMMREGS, ptid,
 			(PTRACE_TYPE_ARG3) xmmregs, 0) == 0)
@@ -255,60 +211,12 @@  i386bsd_store_inferior_registers (struct regcache *regcache, int regnum)
 	return;
     }
 
-#ifdef PT_SETFSBASE
-  if (regnum == -1 || regnum == I386_FSBASE_REGNUM)
-    {
-      register_t base;
-
-      regcache->raw_collect (I386_FSBASE_REGNUM, &base);
-
-      if (gdb_ptrace (PT_SETFSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't write segment register fs_base"));
-      if (regnum != -1)
-	return;
-    }
-#endif
-#ifdef PT_SETGSBASE
-  if (regnum == -1 || regnum == I386_GSBASE_REGNUM)
-    {
-      register_t base;
-
-      regcache->raw_collect (I386_GSBASE_REGNUM, &base);
-
-      if (gdb_ptrace (PT_SETGSBASE, ptid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
-	perror_with_name (_("Couldn't write segment register gs_base"));
-      if (regnum != -1)
-	return;
-    }
-#endif
-
   if (regnum == -1 || regnum >= I386_ST0_REGNUM)
     {
       struct fpreg fpregs;
 #ifdef HAVE_PT_GETXMMREGS
       char xmmregs[512];
-#endif
 
-#ifdef PT_GETXSTATE_INFO
-      if (x86bsd_xsave_len != 0)
-	{
-	  void *xstateregs;
-
-	  xstateregs = alloca (x86bsd_xsave_len);
-	  if (gdb_ptrace (PT_GETXSTATE, ptid,
-			  (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
-	    perror_with_name (_("Couldn't get extended state status"));
-
-	  i387_collect_xsave (regcache, -1, xstateregs, 0);
-
-	  if (gdb_ptrace (PT_SETXSTATE, ptid, (PTRACE_TYPE_ARG3) xstateregs,
-			  x86bsd_xsave_len) == -1)
-	    perror_with_name (_("Couldn't write extended state status"));
-	  return;
-	}
-#endif
-
-#ifdef HAVE_PT_GETXMMREGS
       if (have_ptrace_xmmregs != 0
 	  && gdb_ptrace(PT_GETXMMREGS, ptid,
 			(PTRACE_TYPE_ARG3) xmmregs, 0) == 0)
diff --git a/gdb/i386-fbsd-nat.c b/gdb/i386-fbsd-nat.c
index 9b5913d88e..a67a4267ac 100644
--- a/gdb/i386-fbsd-nat.c
+++ b/gdb/i386-fbsd-nat.c
@@ -29,17 +29,20 @@ 
 
 #include "fbsd-nat.h"
 #include "i386-tdep.h"
+#include "i386-fbsd-tdep.h"
+#include "i387-tdep.h"
 #include "x86-nat.h"
 #include "gdbsupport/x86-xstate.h"
 #include "x86-bsd-nat.h"
-#include "i386-bsd-nat.h"
 
 class i386_fbsd_nat_target final
-  : public i386_bsd_nat_target<fbsd_nat_target>
+  : public x86bsd_nat_target<fbsd_nat_target>
 {
 public:
-  /* Add some extra features to the common *BSD/i386 target.  */
-#ifdef PT_GETXSTATE_INFO
+  void fetch_registers (struct regcache *, int) override;
+  void store_registers (struct regcache *, int) override;
+
+#if defined(PT_GETXMMREGS) || defined(PT_GETXSTATE_INFO)
   const struct target_desc *read_description () override;
 #endif
 
@@ -52,6 +55,192 @@  class i386_fbsd_nat_target final
 
 static i386_fbsd_nat_target the_i386_fbsd_nat_target;
 
+#ifdef PT_GETXSTATE_INFO
+static size_t xsave_len;
+#endif
+
+#ifdef HAVE_PT_GETXMMREGS
+static int have_ptrace_xmmregs;
+#endif
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers.  */
+
+void
+i386_fbsd_nat_target::fetch_registers (struct regcache *regcache, int regnum)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+#if defined(PT_GETFSBASE) || defined(PT_GETGSBASE)
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+#endif
+  pid_t pid = get_ptrace_pid (regcache->ptid ());
+
+  if (fetch_register_set<struct reg> (regcache, regnum, PT_GETREGS,
+				      &i386_fbsd_gregset))
+    {
+      if (regnum != -1)
+	return;
+    }
+
+#ifdef PT_GETFSBASE
+  if (regnum == -1 || regnum == I386_FSBASE_REGNUM)
+    {
+      register_t base;
+
+      if (ptrace (PT_GETFSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't get segment register fs_base"));
+
+      regcache->raw_supply (I386_FSBASE_REGNUM, &base);
+      if (regnum != -1)
+	return;
+    }
+#endif
+#ifdef PT_GETGSBASE
+  if (regnum == -1 || regnum == I386_GSBASE_REGNUM)
+    {
+      register_t base;
+
+      if (ptrace (PT_GETGSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't get segment register gs_base"));
+
+      regcache->raw_supply (I386_GSBASE_REGNUM, &base);
+      if (regnum != -1)
+	return;
+    }
+#endif
+
+  /* There is no i386_fxsave_supplies or i386_xsave_supplies.
+     Instead, the earlier register sets return early if the request
+     was for a specific register that was already satisified to avoid
+     fetching the FPU/XSAVE state unnecessarily.  */
+
+#ifdef PT_GETXSTATE_INFO
+  if (xsave_len != 0)
+    {
+      void *xstateregs = alloca (xsave_len);
+
+      if (ptrace (PT_GETXSTATE, pid, (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+	perror_with_name (_("Couldn't get extended state status"));
+
+      i387_supply_xsave (regcache, regnum, xstateregs);
+      return;
+    }
+#endif
+#ifdef HAVE_PT_GETXMMREGS
+  if (have_ptrace_xmmregs != 0)
+    {
+      char xmmregs[I387_SIZEOF_FXSAVE];
+
+      if (ptrace(PT_GETXMMREGS, pid, (PTRACE_TYPE_ARG3) xmmregs, 0) == -1)
+	perror_with_name (_("Couldn't get XMM registers"));
+
+      i387_supply_fxsave (regcache, regnum, xmmregs);
+      return;
+    }
+#endif
+
+  struct fpreg fpregs;
+
+  if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+    perror_with_name (_("Couldn't get floating point status"));
+
+  i387_supply_fsave (regcache, regnum, &fpregs);
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers.  */
+
+void
+i386_fbsd_nat_target::store_registers (struct regcache *regcache, int regnum)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+#if defined(PT_GETFSBASE) || defined(PT_GETGSBASE)
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+#endif
+  pid_t pid = get_ptrace_pid (regcache->ptid ());
+
+  if (store_register_set<struct reg> (regcache, regnum, PT_GETREGS, PT_SETREGS,
+				      &i386_fbsd_gregset))
+    {
+      if (regnum != -1)
+	return;
+    }
+
+#ifdef PT_SETFSBASE
+  if (regnum == -1 || regnum == I386_FSBASE_REGNUM)
+    {
+      register_t base;
+
+      regcache->raw_collect (I386_FSBASE_REGNUM, &base);
+
+      if (ptrace (PT_SETFSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't write segment register fs_base"));
+      if (regnum != -1)
+	return;
+    }
+#endif
+#ifdef PT_SETGSBASE
+  if (regnum == -1 || regnum == I386_GSBASE_REGNUM)
+    {
+      register_t base;
+
+      regcache->raw_collect (I386_GSBASE_REGNUM, &base);
+
+      if (ptrace (PT_SETGSBASE, pid, (PTRACE_TYPE_ARG3) &base, 0) == -1)
+	perror_with_name (_("Couldn't write segment register gs_base"));
+      if (regnum != -1)
+	return;
+    }
+#endif
+
+  /* There is no i386_fxsave_supplies or i386_xsave_supplies.
+     Instead, the earlier register sets return early if the request
+     was for a specific register that was already satisified to avoid
+     fetching the FPU/XSAVE state unnecessarily.  */
+
+#ifdef PT_GETXSTATE_INFO
+  if (xsave_len != 0)
+    {
+      void *xstateregs = alloca (xsave_len);
+
+      if (ptrace (PT_GETXSTATE, pid, (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+	perror_with_name (_("Couldn't get extended state status"));
+
+      i387_collect_xsave (regcache, regnum, xstateregs, 0);
+
+      if (ptrace (PT_SETXSTATE, pid, (PTRACE_TYPE_ARG3) xstateregs, xsave_len)
+	  == -1)
+	perror_with_name (_("Couldn't write extended state status"));
+      return;
+    }
+#endif
+#ifdef HAVE_PT_GETXMMREGS
+  if (have_ptrace_xmmregs != 0)
+    {
+      char xmmregs[I387_SIZEOF_FXSAVE];
+
+      if (ptrace(PT_GETXMMREGS, pid, (PTRACE_TYPE_ARG3) xmmregs, 0) == -1)
+	perror_with_name (_("Couldn't get XMM registers"));
+
+      i387_collect_fxsave (regcache, regnum, xmmregs);
+
+      if (ptrace (PT_SETXMMREGS, pid, (PTRACE_TYPE_ARG3) xmmregs, 0) == -1)
+	perror_with_name (_("Couldn't write XMM registers"));
+      return;
+    }
+#endif
+
+  struct fpreg fpregs;
+
+  if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+    perror_with_name (_("Couldn't get floating point status"));
+
+  i387_collect_fsave (regcache, regnum, &fpregs);
+
+  if (ptrace (PT_SETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+    perror_with_name (_("Couldn't write floating point status"));
+}
+
 /* Resume execution of the inferior process.  If STEP is nonzero,
    single-step it.  If SIGNAL is nonzero, give it that signal.  */
 
@@ -135,15 +324,21 @@  i386fbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb)
 }
 
 
-#ifdef PT_GETXSTATE_INFO
+#if defined(PT_GETXMMREGS) || defined(PT_GETXSTATE_INFO)
 /* Implement the read_description method.  */
 
 const struct target_desc *
 i386_fbsd_nat_target::read_description ()
 {
+#ifdef PT_GETXSTATE_INFO
   static int xsave_probed;
   static uint64_t xcr0;
+#endif
+#ifdef PT_GETXMMREGS
+  static int xmm_probed;
+#endif
 
+#ifdef PT_GETXSTATE_INFO
   if (!xsave_probed)
     {
       struct ptrace_xstate_info info;
@@ -151,16 +346,32 @@  i386_fbsd_nat_target::read_description ()
       if (ptrace (PT_GETXSTATE_INFO, inferior_ptid.pid (),
 		  (PTRACE_TYPE_ARG3) &info, sizeof (info)) == 0)
 	{
-	  x86bsd_xsave_len = info.xsave_len;
+	  xsave_len = info.xsave_len;
 	  xcr0 = info.xsave_mask;
 	}
       xsave_probed = 1;
     }
 
-  if (x86bsd_xsave_len == 0)
-    xcr0 = X86_XSTATE_SSE_MASK;
+  if (xsave_len != 0)
+    return i386_target_description (xcr0, true);
+#endif
 
-  return i386_target_description (xcr0, true);
+#ifdef PT_GETXMMREGS
+  if (!xmm_probed)
+    {
+      char xmmregs[I387_SIZEOF_FXSAVE];
+
+      if (ptrace (PT_GETXMMREGS, inferior_ptid.pid (),
+		  (PTRACE_TYPE_ARG3) xmmregs, 0) == 0)
+	have_ptrace_xmmregs = 1;
+      xmm_probed = 1;
+    }
+
+  if (have_ptrace_xmmregs)
+    return i386_target_description (X86_XSTATE_SSE_MASK, true);
+#endif
+
+  return i386_target_description (X86_XSTATE_X87_MASK, true);
 }
 #endif
 
diff --git a/gdb/i386-fbsd-tdep.h b/gdb/i386-fbsd-tdep.h
index c2bbda60e8..6cb05073e7 100644
--- a/gdb/i386-fbsd-tdep.h
+++ b/gdb/i386-fbsd-tdep.h
@@ -20,6 +20,8 @@ 
 #ifndef I386_FBSD_TDEP_H
 #define I386_FBSD_TDEP_H
 
+#include "regset.h"
+
 /* Get XSAVE extended state xcr0 from core dump.  */
 extern uint64_t i386fbsd_core_read_xcr0 (bfd *abfd);
 
@@ -28,4 +30,6 @@  extern uint64_t i386fbsd_core_read_xcr0 (bfd *abfd);
    matches the layout on Linux.  */
 #define I386_FBSD_XSAVE_XCR0_OFFSET 464
 
+extern const struct regset i386_fbsd_gregset;
+
 #endif /* i386-fbsd-tdep.h */
diff --git a/gdb/x86-bsd-nat.c b/gdb/x86-bsd-nat.c
index 453fc44116..7e432b7e91 100644
--- a/gdb/x86-bsd-nat.c
+++ b/gdb/x86-bsd-nat.c
@@ -46,10 +46,6 @@  gdb_ptrace (PTRACE_TYPE_ARG1 request, ptid_t ptid, PTRACE_TYPE_ARG3 addr)
 #endif
 }
 
-#ifdef PT_GETXSTATE_INFO
-size_t x86bsd_xsave_len;
-#endif
-
 /* Support for debug registers.  */
 
 #ifdef HAVE_PT_GETDBREGS
diff --git a/gdb/x86-bsd-nat.h b/gdb/x86-bsd-nat.h
index 02d61c20b0..d4b929ba8a 100644
--- a/gdb/x86-bsd-nat.h
+++ b/gdb/x86-bsd-nat.h
@@ -22,9 +22,6 @@ 
 
 #include "x86-nat.h"
 
-/* Low level x86 XSAVE info.  */
-extern size_t x86bsd_xsave_len;
-
 /* A prototype *BSD/x86 target.  */
 
 template<typename BaseTarget>