diff mbox

[committed] MIPS/Linux: Correct o32 core file FGR interpretation

Message ID alpine.DEB.2.00.1805241126080.10896@tp.orcam.me.uk
State Committed
Headers show

Commit Message

Maciej W. Rozycki May 25, 2018, 11:41 a.m. UTC
Our interpretation of the layout of floating-point general registers 
(FGRs) in o32 MIPS/Linux core files is different from how the kernel 
makes them, affecting the CP0 Status.FR=0 aka FP32 mode (we don't 
currently support the CP0 Status.FR=1 aka FP64 mode with the o32 ABI).  

In the FP32 mode pairs of consecutive even/odd-numbered 32-bit registers 
are placed together as 64-bit values in even-indexed 64-bit slots 
corresponding to the even index, leaving the odd-indexed 64-bit slots 
unused.  These 64-bit values are stored according to the endianness in 
effect, which is how the MIPS II SDC1 instruction would store them.

It has always been like that with the Linux kernel for MIPS II and 
higher ISA processors, which are the vast majority ever supported, as it 
is indeed SDC1 that the kernel uses to store FGRs in a floating-point 
context.

With MIPS I processors, which lack the SDC1 instruction, a layout that 
we expect used to be used long ago, but it was corrected for consistency 
with newer processors back in 2002, with `linux-mips.org' (LMO) commit 
42533948caac ("Major pile of FP emulator changes."), the fix corrected 
with LMO commit 849fa7a50dff ("R3k FPU ptrace() handling fixes."), and 
then broken and fixed over and over again, until last time fixed with 
commit 80cbfad79096 ("MIPS: Correct MIPS I FP context layout").

Consequently the values we see in FP32 core files or produce with the 
`gcore' command are different from those obtained from the same FP 
context of a live process, e.g. with a big-endian configuration these 
live values:

(gdb) info registers float
f0:  0x4b5c6d7e flt: 14445950          dbl: 1.7446153562345001e-274
f1:  0x0718293a flt: 1.14473244e-34
f2:  0xc3d4e5f6 flt: -425.79657        dbl: -1.046160437414959e-233
f3:  0x8f90a1b2 flt: -1.42617791e-29
f4:  0x4c5d6e7f flt: 58046972          dbl: 1.1908587841220294e-269
f5:  0x08192a3b flt: 4.60914044e-34
f6:  0xc4d5e6f7 flt: -1711.21765       dbl: -6.2784661835068965e-306
f7:  0x8091a2b3 flt: -1.33745124e-38
f8:  0x45566778 flt: 3430.4668         dbl: 1.6530355595710607e-303
f9:  0x01122334 flt: 2.68412219e-38
f10: 0xcddeeff0 flt: -467533312        dbl: -2.1174864564135575e-262
f11: 0x899aabbc flt: -3.72356497e-33
f12: 0x46576879 flt: 13786.1182        dbl: 1.143296486773654e-298
f13: 0x02132435 flt: 1.08102453e-37
f14: 0xcedfe0f1 flt: -1.87803046e+09   dbl: -1.4399511533369862e-257
f15: 0x8a9bacbd flt: -1.4990934e-32
f16: 0x4758697a flt: 55401.4766        dbl: 7.8856820439568725e-294
f17: 0x03142536 flt: 4.3536007e-37
f18: 0xcfd0e1f2 flt: -7.00893696e+09   dbl: -9.7791926757340559e-253
f19: 0x8b9cadbe flt: -6.03504325e-32
f20: 0x48596a7b flt: 222633.922        dbl: 5.4255001483306113e-289
f21: 0x04152637 flt: 1.75324132e-36
f22: 0xc0d1e2f3 flt: -6.55895376       dbl: -6.6332401002310683e-248
f23: 0x8c9daebf flt: -2.42948516e-31
f24: 0x495a6b7c flt: 894647.75         dbl: 3.7244369058749787e-284
f25: 0x05162738 flt: 7.06016945e-36
f26: 0xc1d2e3f4 flt: -26.3613052       dbl: -4.4941535759306202e-243
f27: 0x8d9eafb0 flt: -9.77979703e-31
f28: 0x4a5b6c7d flt: 3595039.25        dbl: 2.5514593711161396e-279
f29: 0x06172839 flt: 2.84294945e-35
f30: 0xc2d3e4f5 flt: -105.947182       dbl: -3.035646690850097e-238
f31: 0x8e9fa0b1 flt: -3.93512664e-30
fcsr: 0x0
fir: 0xf30000
(gdb) 

show up in a core file as these:

(gdb) info registers float
f0:  0x0718293a flt: 1.14473244e-34    dbl: nan
f1:  0x7ff80000 flt: nan
f2:  0x8f90a1b2 flt: -1.42617791e-29   dbl: nan
f3:  0x7ff80000 flt: nan
f4:  0x08192a3b flt: 4.60914044e-34    dbl: nan
f5:  0x7ff80000 flt: nan
f6:  0x8091a2b3 flt: -1.33745124e-38   dbl: nan
f7:  0x7ff80000 flt: nan
f8:  0x01122334 flt: 2.68412219e-38    dbl: nan
f9:  0x7ff80000 flt: nan
f10: 0x899aabbc flt: -3.72356497e-33   dbl: nan
f11: 0x7ff80000 flt: nan
f12: 0x02132435 flt: 1.08102453e-37    dbl: nan
f13: 0x7ff80000 flt: nan
f14: 0x8a9bacbd flt: -1.4990934e-32    dbl: nan
f15: 0x7ff80000 flt: nan
f16: 0x03142536 flt: 4.3536007e-37     dbl: nan
f17: 0x7ff80000 flt: nan
f18: 0x8b9cadbe flt: -6.03504325e-32   dbl: nan
f19: 0x7ff80000 flt: nan
f20: 0x04152637 flt: 1.75324132e-36    dbl: nan
f21: 0x7ff80000 flt: nan
f22: 0x8c9daebf flt: -2.42948516e-31   dbl: nan
f23: 0x7ff80000 flt: nan
f24: 0x05162738 flt: 7.06016945e-36    dbl: nan
f25: 0x7ff80000 flt: nan
f26: 0x8d9eafb0 flt: -9.77979703e-31   dbl: nan
f27: 0x7ff80000 flt: nan
f28: 0x06172839 flt: 2.84294945e-35    dbl: nan
f29: 0x7ff80000 flt: nan
f30: 0x8e9fa0b1 flt: -3.93512664e-30   dbl: nan
f31: 0x7ff80000 flt: nan
(gdb) 

Notice how values from odd-numbered registers are shown in corresponding 
even-numbered registers and how dummy 0x7ff80000 NaN values, which the 
kernel places in unused slots, are reported in odd-numbered registers.

Correct our intepretation then, to match the kernel's.  As it happens 
the o32 FGR core file representation matches that used by the `ptrace' 
PTRACE_GETFPREGS request, which means our 64-bit handlers can be readily 
used, as they already correctly handle the differences between o32 FP32 
mode vs n32/n64 representations.

Adjust comments accordingly throughout, in particular remove a reference 
to the r3000/tx39 MIPS I processor peculiarity, long irrelevant.

Add a test case to verify correctness.  Avoid GCC bugs and limitations 
in the test case where possible; the test case still fails to build with 
GCC 8 and the o32 FP64 mode (i.e. with `-mips32r2 -mfp64' options) 
giving:

mips-fpregset-core.c: In function 'main':
mips-fpregset-core.c:66:3: error: inconsistent operand constraints in an 'asm'
   asm (
   ^~~

(GCC PR target/85909), but that is not a concern for us as yet, because 
as noted above we do not currently support the o32 FP64 mode anyway.

	gdb/
	* mips-linux-tdep.h (mips_supply_fpregset, mips_fill_fpregset): 
	Remove prototypes.
	* mips-linux-nat.c (supply_fpregset): Always call 
	`mips64_supply_fpregset' rather than `mips_supply_fpregset'.
	(fill_fpregset): Always call `mips64_fill_fpregset' rather than
	`mips_fill_fpregset'.
	* mips-linux-tdep.c (mips_supply_fpregset)
	(mips_supply_fpregset_wrapper, mips_fill_fpregset)
	(mips_fill_fpregset_wrapper): Remove functions.
	(mips64_supply_fpregset, mips64_fill_fpregset): Update comments.
	(mips_linux_fpregset): Remove variable.
	(mips_linux_iterate_over_regset_sections): Use 
	`mips64_linux_fpregset' in place of `mips_linux_fpregset'.
	(mips_linux_o32_sigframe_init): Remove comment.

	gdb/testsuite/
	* gdb.arch/mips-fpregset-core.exp: New test.
	* gdb.arch/mips-fpregset-core.c: New test source.
---
Hi,

 Verified native and native-gdbserver across o32, n32 and n64.  Committed.

  Maciej
---
 gdb/mips-linux-nat.c                          |   12 --
 gdb/mips-linux-tdep.c                         |  109 ++----------------
 gdb/mips-linux-tdep.h                         |    2 
 gdb/testsuite/gdb.arch/mips-fpregset-core.c   |   82 +++++++++++++
 gdb/testsuite/gdb.arch/mips-fpregset-core.exp |  154 ++++++++++++++++++++++++++
 5 files changed, 251 insertions(+), 108 deletions(-)

gdb-mips-linux-fpregset.diff
diff mbox

Patch

Index: binutils/gdb/mips-linux-nat.c
===================================================================
--- binutils.orig/gdb/mips-linux-nat.c	2018-05-23 17:10:05.000000000 +0100
+++ binutils/gdb/mips-linux-nat.c	2018-05-24 20:41:41.377061555 +0100
@@ -224,22 +224,14 @@  fill_gregset (const struct regcache *reg
 void
 supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
 {
-  if (mips_isa_regsize (regcache->arch ()) == 4)
-    mips_supply_fpregset (regcache, (const mips_elf_fpregset_t *) fpregsetp);
-  else
-    mips64_supply_fpregset (regcache,
-			    (const mips64_elf_fpregset_t *) fpregsetp);
+  mips64_supply_fpregset (regcache, (const mips64_elf_fpregset_t *) fpregsetp);
 }
 
 void
 fill_fpregset (const struct regcache *regcache,
 	       gdb_fpregset_t *fpregsetp, int regno)
 {
-  if (mips_isa_regsize (regcache->arch ()) == 4)
-    mips_fill_fpregset (regcache, (mips_elf_fpregset_t *) fpregsetp, regno);
-  else
-    mips64_fill_fpregset (regcache,
-			  (mips64_elf_fpregset_t *) fpregsetp, regno);
+  mips64_fill_fpregset (regcache, (mips64_elf_fpregset_t *) fpregsetp, regno);
 }
 
 
Index: binutils/gdb/mips-linux-tdep.c
===================================================================
--- binutils.orig/gdb/mips-linux-tdep.c	2018-05-24 11:06:44.384139557 +0100
+++ binutils/gdb/mips-linux-tdep.c	2018-05-24 20:54:14.911206975 +0100
@@ -233,82 +233,6 @@  mips_fill_gregset_wrapper (const struct 
   mips_fill_gregset (regcache, (mips_elf_gregset_t *)gregs, regnum);
 }
 
-/* Likewise, unpack an elf_fpregset_t.  */
-
-void
-mips_supply_fpregset (struct regcache *regcache,
-		      const mips_elf_fpregset_t *fpregsetp)
-{
-  struct gdbarch *gdbarch = regcache->arch ();
-  int regi;
-
-  for (regi = 0; regi < 32; regi++)
-    regcache_raw_supply (regcache,
-			 gdbarch_fp0_regnum (gdbarch) + regi,
-			 *fpregsetp + regi);
-
-  regcache_raw_supply (regcache,
-		       mips_regnum (gdbarch)->fp_control_status,
-		       *fpregsetp + 32);
-
-  /* FIXME: how can we supply FCRIR?  The ABI doesn't tell us.  */
-  regcache->raw_supply_zeroed
-    (mips_regnum (gdbarch)->fp_implementation_revision);
-}
-
-static void
-mips_supply_fpregset_wrapper (const struct regset *regset,
-			      struct regcache *regcache,
-			      int regnum, const void *gregs, size_t len)
-{
-  gdb_assert (len >= sizeof (mips_elf_fpregset_t));
-
-  mips_supply_fpregset (regcache, (const mips_elf_fpregset_t *)gregs);
-}
-
-/* Likewise, pack one or all floating point registers into an
-   elf_fpregset_t.  */
-
-void
-mips_fill_fpregset (const struct regcache *regcache,
-		    mips_elf_fpregset_t *fpregsetp, int regno)
-{
-  struct gdbarch *gdbarch = regcache->arch ();
-  char *to;
-
-  if ((regno >= gdbarch_fp0_regnum (gdbarch))
-      && (regno < gdbarch_fp0_regnum (gdbarch) + 32))
-    {
-      to = (char *) (*fpregsetp + regno - gdbarch_fp0_regnum (gdbarch));
-      regcache_raw_collect (regcache, regno, to);
-    }
-  else if (regno == mips_regnum (gdbarch)->fp_control_status)
-    {
-      to = (char *) (*fpregsetp + 32);
-      regcache_raw_collect (regcache, regno, to);
-    }
-  else if (regno == -1)
-    {
-      int regi;
-
-      for (regi = 0; regi < 32; regi++)
-	mips_fill_fpregset (regcache, fpregsetp,
-			    gdbarch_fp0_regnum (gdbarch) + regi);
-      mips_fill_fpregset (regcache, fpregsetp,
-			  mips_regnum (gdbarch)->fp_control_status);
-    }
-}
-
-static void
-mips_fill_fpregset_wrapper (const struct regset *regset,
-			    const struct regcache *regcache,
-			    int regnum, void *gregs, size_t len)
-{
-  gdb_assert (len >= sizeof (mips_elf_fpregset_t));
-
-  mips_fill_fpregset (regcache, (mips_elf_fpregset_t *)gregs, regnum);
-}
-
 /* Support for 64-bit ABIs.  */
 
 /* Figure out where the longjmp will land.
@@ -473,7 +397,16 @@  mips64_fill_gregset_wrapper (const struc
   mips64_fill_gregset (regcache, (mips64_elf_gregset_t *)gregs, regnum);
 }
 
-/* Likewise, unpack an elf_fpregset_t.  */
+/* Likewise, unpack an elf_fpregset_t.  Linux only uses even-numbered
+   FPR slots in the Status.FR=0 mode, storing even-odd FPR pairs as the
+   SDC1 instruction would.  When run on MIPS I architecture processors
+   all FPR slots used to be used, unusually, holding the respective FPRs
+   in the first 4 bytes, but that was corrected for consistency, with
+   `linux-mips.org' (LMO) commit 42533948caac ("Major pile of FP emulator
+   changes."), the fix corrected with LMO commit 849fa7a50dff ("R3k FPU
+   ptrace() handling fixes."), and then broken and fixed over and over
+   again, until last time fixed with commit 80cbfad79096 ("MIPS: Correct
+   MIPS I FP context layout").  */
 
 void
 mips64_supply_fpregset (struct regcache *regcache,
@@ -482,8 +415,6 @@  mips64_supply_fpregset (struct regcache 
   struct gdbarch *gdbarch = regcache->arch ();
   int regi;
 
-  /* See mips_linux_o32_sigframe_init for a description of the
-     peculiar FP register layout.  */
   if (register_size (gdbarch, gdbarch_fp0_regnum (gdbarch)) == 4)
     for (regi = 0; regi < 32; regi++)
       {
@@ -523,7 +454,8 @@  mips64_supply_fpregset_wrapper (const st
 }
 
 /* Likewise, pack one or all floating point registers into an
-   elf_fpregset_t.  */
+   elf_fpregset_t.  See `mips_supply_fpregset' for an explanation
+   of the layout.  */
 
 void
 mips64_fill_fpregset (const struct regcache *regcache,
@@ -535,8 +467,6 @@  mips64_fill_fpregset (const struct regca
   if ((regno >= gdbarch_fp0_regnum (gdbarch))
       && (regno < gdbarch_fp0_regnum (gdbarch) + 32))
     {
-      /* See mips_linux_o32_sigframe_init for a description of the
-	 peculiar FP register layout.  */
       if (register_size (gdbarch, regno) == 4)
 	{
 	  int regi = regno - gdbarch_fp0_regnum (gdbarch);
@@ -597,11 +527,6 @@  static const struct regset mips64_linux_
     NULL, mips64_supply_gregset_wrapper, mips64_fill_gregset_wrapper
   };
 
-static const struct regset mips_linux_fpregset =
-  {
-    NULL, mips_supply_fpregset_wrapper, mips_fill_fpregset_wrapper
-  };
-
 static const struct regset mips64_linux_fpregset =
   {
     NULL, mips64_supply_fpregset_wrapper, mips64_fill_fpregset_wrapper
@@ -617,7 +542,7 @@  mips_linux_iterate_over_regset_sections 
     {
       cb (".reg", sizeof (mips_elf_gregset_t), &mips_linux_gregset,
 	  NULL, cb_data);
-      cb (".reg2", sizeof (mips_elf_fpregset_t), &mips_linux_fpregset,
+      cb (".reg2", sizeof (mips64_elf_fpregset_t), &mips64_linux_fpregset,
 	  NULL, cb_data);
     }
   else
@@ -1076,14 +1001,6 @@  mips_linux_o32_sigframe_init (const stru
 			     (regs_base + SIGCONTEXT_REGS
 			      + ireg * SIGCONTEXT_REG_SIZE));
 
-  /* The way that floating point registers are saved, unfortunately,
-     depends on the architecture the kernel is built for.  For the r3000 and
-     tx39, four bytes of each register are at the beginning of each of the
-     32 eight byte slots.  For everything else, the registers are saved
-     using double precision; only the even-numbered slots are initialized,
-     and the high bits are the odd-numbered register.  Assume the latter
-     layout, since we can't tell, and it's much more common.  Which bits are
-     the "high" bits depends on endianness.  */
   for (ireg = 0; ireg < 32; ireg++)
     if ((gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) != (ireg & 1))
       trad_frame_set_reg_addr (this_cache,
Index: binutils/gdb/mips-linux-tdep.h
===================================================================
--- binutils.orig/gdb/mips-linux-tdep.h	2018-02-23 14:14:31.000000000 +0000
+++ binutils/gdb/mips-linux-tdep.h	2018-05-24 20:42:43.391199979 +0100
@@ -52,8 +52,6 @@  typedef mips_elf_fpreg_t mips_elf_fpregs
 
 void mips_supply_gregset (struct regcache *, const mips_elf_gregset_t *);
 void mips_fill_gregset (const struct regcache *, mips_elf_gregset_t *, int);
-void mips_supply_fpregset (struct regcache *, const mips_elf_fpregset_t *);
-void mips_fill_fpregset (const struct regcache *, mips_elf_fpregset_t *, int);
 
 /* 64-bit support.  */
 
Index: binutils/gdb/testsuite/gdb.arch/mips-fpregset-core.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/gdb/testsuite/gdb.arch/mips-fpregset-core.c	2018-05-24 13:52:43.065613141 +0100
@@ -0,0 +1,82 @@ 
+/* This test is part of GDB, the GNU debugger.
+
+   Copyright 2018 Free Software Foundation, Inc.
+
+   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 <stdint.h>
+
+int __attribute__ ((nomips16))
+main (void)
+{
+  /* We need to use a complex type to avoid hitting GCC's limit of
+     the number of `asm' operands:
+
+     mips-fpregset-core.f: In function 'main':
+     mips-fpregset-core.f:66:3: error: more than 30 operands in 'asm'
+        asm (
+        ^~~
+
+     and still have complete 32 FGR coverage with FP64 ABIs.  Using
+     a complex type breaks o32 GCC though, so use plain `double' with
+     FP32.  */
+#if _MIPS_FPSET == 32
+  typedef double _Complex float_t;
+#else
+  typedef double float_t;
+#endif
+  union
+    {
+      uint64_t i[32];
+      float_t f[256 / sizeof (float_t)];
+    }
+  u =
+    { .i =
+      {
+	0x0112233445566778, 0x899aabbccddeeff0,
+	0x0213243546576879, 0x8a9bacbdcedfe0f1,
+	0x031425364758697a, 0x8b9cadbecfd0e1f2,
+	0x0415263748596a7b, 0x8c9daebfc0d1e2f3,
+	0x05162738495a6b7c, 0x8d9eafb0c1d2e3f4,
+	0x061728394a5b6c7d, 0x8e9fa0b1c2d3e4f5,
+	0x0718293a4b5c6d7e, 0x8f90a1b2c3d4e5f6,
+	0x08192a3b4c5d6e7f, 0x8091a2b3c4d5e6f7,
+	0x091a2b3c4d5e6f70, 0x8192a3b4c5d6e7f8,
+	0x0a1b2c3d4e5f6071, 0x8293a4b5c6d7e8f9,
+	0x0b1c2d3e4f506172, 0x8394a5b6c7d8e9fa,
+	0x0c1d2e3f40516273, 0x8495a6b7c8d9eafb,
+	0x0d1e2f3041526374, 0x8596a7b8c9daebfc,
+	0x0e1f203142536475, 0x8697a8b9cadbecfd,
+	0x0f10213243546576, 0x8798a9bacbdcedfe,
+	0x0011223344556677, 0x8899aabbccddeeff
+      }
+    };
+
+  asm (
+    ".globl\tbreak_here\n\t"
+    ".aent\tbreak_here\n"
+    "break_here:\n\t"
+    "lb\t$0,0($0)\n"
+    :
+    : [f0] "f" (u.f[0]), [f2] "f" (u.f[1]),
+      [f4] "f" (u.f[2]), [f6] "f" (u.f[3]),
+      [f8] "f" (u.f[4]), [f10] "f" (u.f[5]),
+      [f12] "f" (u.f[6]), [f14] "f" (u.f[7]),
+      [f16] "f" (u.f[8]), [f18] "f" (u.f[9]),
+      [f20] "f" (u.f[10]), [f22] "f" (u.f[11]),
+      [f24] "f" (u.f[12]), [f26] "f" (u.f[13]),
+      [f28] "f" (u.f[14]), [f30] "f" (u.f[15]));
+
+  return 0;
+}
Index: binutils/gdb/testsuite/gdb.arch/mips-fpregset-core.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/gdb/testsuite/gdb.arch/mips-fpregset-core.exp	2018-05-24 18:12:19.286363209 +0100
@@ -0,0 +1,154 @@ 
+# Copyright 2018 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test MIPS Floating Point General Register handling in core files.
+
+if { ![istarget "mips*-*-*"] } then {
+    verbose "Skipping MIPS Floating Point General Register tests."
+    return
+}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile}] } {
+    return
+}
+
+# Procedure to get current content of all floating-point registers.
+proc mips_fpregset_core_fetch_float_registers { test } {
+    global gdb_prompt
+
+    set all_registers_lines {}
+    set bad -1
+    # Former trailing `\[\r\n\]+' may eat just \r leaving \n in the buffer
+    # corrupting the next matches.
+    if { [gdb_test_multiple "info registers float" $test {
+	-re "info registers float\r\n" {
+	    exp_continue
+	}
+	-ex "The program has no registers now" {
+	    set bad 1
+	    exp_continue
+	}
+	-re "^\(?:fcsr\|fir\):\[ \t\]+\[^\r\n\]+\r\n" {
+	    # Filter out control registers.  They may or may not be a part
+	    # of the float group depending on whether XML descriptions are
+	    # used or not.
+	    exp_continue
+	}
+	-re "^\[^ \t\]+\[ \t\]+\[^\r\n\]+\r\n" {
+	    lappend all_registers_lines $expect_out(0,string)
+	    exp_continue
+	}
+	-re "$gdb_prompt $" {
+	    incr bad
+	}
+	-re "^\[^\r\n\]+\r\n" {
+	    if { !$bad } {
+		warning "Unrecognized output: $expect_out(0,string)"
+		set bad 1
+	    }
+	    exp_continue
+	}
+    }] != 0 } {
+	return {}
+    }
+
+    if { $bad } {
+	fail $test
+	return {}
+    }
+
+    pass $test
+    return $all_registers_lines
+}
+
+# Generate a native core file.
+
+set corefile [core_find $binfile]
+set core_supported [expr {$corefile != ""}]
+
+# Generate a core file with "gcore".
+
+clean_restart ${binfile}
+
+runto break_here
+
+# Check if we have an FPU available.
+gdb_test_multiple "show mipsfpu" "check for MIPS floating-point coprocessor" {
+    -re "The MIPS floating-point coprocessor\
+	 .*\(absent\|unknown\).*$gdb_prompt $" {
+        unsupported "no MIPS floating-point coprocessor in the processor"
+        return
+    }
+    -re "The MIPS floating-point coprocessor .*$gdb_prompt $" {
+        verbose "MIPS floating-point coprocessor check successful."
+    }
+    default {
+        fail
+        return
+    }
+}
+
+# Save live FGR register contents.
+set live_fgr_contents [mips_fpregset_core_fetch_float_registers \
+    "retrieve live FGR register contents"]
+
+set gcorefile [standard_output_file gcore.test]
+set gcore_supported [gdb_gcore_cmd "$gcorefile" "gcore"]
+
+# Restart gdb and load COREFILE as a core file.  SUPPORTED is true iff
+# the core was generated successfully; otherwise, the tests are marked
+# unsupported.
+#
+proc mips_fpregset_core_test { supported corefile } {
+    upvar live_fgr_contents live_fgr_contents
+    upvar target_triplet target_triplet
+    upvar host_triplet host_triplet
+    upvar binfile binfile
+
+    clean_restart ${binfile}
+
+    set test "load core file"
+    if { $supported } {
+	set core_loaded [gdb_core_cmd $corefile $test]
+    } else {
+	set core_loaded 0
+	unsupported $test
+    }
+
+    if { $core_loaded == 1 } {
+	set test "core file FGR register contents"
+	set core_fgr_contents \
+	    [mips_fpregset_core_fetch_float_registers "retrieve $test"]
+	if { $core_fgr_contents == $live_fgr_contents } then {
+	    pass $test
+	} else {
+	    fail $test
+	}
+    } else {
+	unsupported $test
+    }
+}
+
+with_test_prefix "native" {
+    mips_fpregset_core_test $core_supported $corefile
+}
+
+with_test_prefix "gcore" {
+    mips_fpregset_core_test $gcore_supported $gcorefile
+}
+
+gdb_exit