MIPS/Linux: Correct o32 core file FGR interpretation
authorMaciej W. Rozycki <macro@mips.com>
Fri, 25 May 2018 11:37:45 +0000 (12:37 +0100)
committerMaciej W. Rozycki <macro@mips.com>
Fri, 25 May 2018 11:37:45 +0000 (12:37 +0100)
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.

gdb/ChangeLog
gdb/gdbserver/ChangeLog
gdb/mips-linux-nat.c
gdb/mips-linux-tdep.c
gdb/mips-linux-tdep.h
gdb/testsuite/gdb.arch/mips-fpregset-core.c [new file with mode: 0644]
gdb/testsuite/gdb.arch/mips-fpregset-core.exp [new file with mode: 0644]

index 4f42528a06cf3fe7247ea232c9ce66ef37dfcd14..070c991f84fbf6fa18235f3a43efdd5c45a362e1 100644 (file)
@@ -1,3 +1,20 @@
+2018-05-25  Maciej W. Rozycki  <macro@mips.com>
+
+       * 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.
+
 2018-05-25  Pedro Alves  <palves@redhat.com>
 
        * remote.c (struct vCont_action_support, MAXTHREADLISTRESULTS)
index 7aa59467ac3ffbe386b3ec4401cc6fd929a78a03..02248186e7897871a25cccd0f8dad0ba0fb0523f 100644 (file)
@@ -1,3 +1,8 @@
+2018-05-25  Maciej W. Rozycki  <macro@mips.com>
+
+       * gdb.arch/mips-fpregset-core.exp: New test.
+       * gdb.arch/mips-fpregset-core.c: New test source.
+
 2018-05-23  Erik Kurzinger  <ekurzinger@nvidia.com>
 
        PR server/23198
index a9f0b790769e27124515a5a05707ea80045acb23..36831071fbc493554f243a5be83f898714a0f69f 100644 (file)
@@ -224,22 +224,14 @@ fill_gregset (const struct regcache *regcache,
 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 175b4024d168a62bd9a0d6a9bbe63074934faa98..e5804246855939039ff270c65897e13e8e7b40f0 100644 (file)
@@ -233,82 +233,6 @@ mips_fill_gregset_wrapper (const struct regset *regset,
   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 struct regset *regset,
   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 *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 struct regset *regset,
 }
 
 /* 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 regcache *regcache,
   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_gregset =
     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 (struct gdbarch *gdbarch,
     {
       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 struct tramp_frame *self,
                             (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 83636fab9e3b96e6179291d5f31184656abc0541..a87d555e57f311796a6eeabc9b58bb4562b74df1 100644 (file)
@@ -52,8 +52,6 @@ typedef mips_elf_fpreg_t mips_elf_fpregset_t[ELF_NFPREG];
 
 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.  */
 
diff --git a/gdb/testsuite/gdb.arch/mips-fpregset-core.c b/gdb/testsuite/gdb.arch/mips-fpregset-core.c
new file mode 100644 (file)
index 0000000..f0ac31b
--- /dev/null
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.arch/mips-fpregset-core.exp b/gdb/testsuite/gdb.arch/mips-fpregset-core.exp
new file mode 100644 (file)
index 0000000..49fcb79
--- /dev/null
@@ -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