This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch master updated. glibc-2.20-457-g0bd9567


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  0bd956720c457ff054325b48f26ac7c91cb060e8 (commit)
      from  f59ad976ed979d22637c5187f6a92fbbd8c191e4 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=0bd956720c457ff054325b48f26ac7c91cb060e8

commit 0bd956720c457ff054325b48f26ac7c91cb060e8
Author: Matthew Fortune <matthew.fortune@imgtec.com>
Date:   Sun Jun 1 00:16:25 2014 +0100

    Add support for MIPS O32 FPXX and .MIPS.abiflags
    
    	* elf/elf.h (PT_MIPS_ABIFLAGS): Define.
    	(Elf_MIPS_ABIFlags_v0): New structure.
    	(EF_MIPS_FP64): Define.
    	(MIPS_AFL_REG_NONE, MIPS_AFL_REG_32, MIPS_AFL_REG_64): Likewise.
    	(MIPS_AFL_REG_128, MIPS_AFL_ASE_DSP, MIPS_AFL_ASE_DSP64): Likewise.
    	(MIPS_AFL_ASE_DSPR2, MIPS_AFL_ASE_EVA, MIPS_AFL_ASE_MCU): Likewise.
    	(MIPS_AFL_ASE_MDMX, MIPS_AFL_ASE_MIPS3D, MIPS_AFL_ASE_MT): Likewise.
    	(MIPS_AFL_ASE_SMARTMIPS, MIPS_AFL_ASE_VIRT): Likewise.
    	(MIPS_AFL_ASE_VIRT64, MIPS_AFL_ASE_MSA, MIPS_AFL_ASE_MSA64): Likewise.
    	(MIPS_AFL_ASE_MIPS16, MIPS_AFL_ASE_MICROMIPS): Likewise.
    	(MIPS_AFL_ASE_XPA, MIPS_AFL_EXT_XLR, MIPS_AFL_EXT_OCTEON2): Likewise.
    	(MIPS_AFL_EXT_OCTEONP, MIPS_AFL_EXT_LOONGSON_3A): Likewise.
    	(MIPS_AFL_EXT_OCTEON, MIPS_AFL_EXT_5900, MIPS_AFL_EXT_4010): Likewise.
    	(MIPS_AFL_EXT_4100, MIPS_AFL_EXT_3900, MIPS_AFL_EXT_10000): Likewise.
    	(MIPS_AFL_EXT_SB1, MIPS_AFL_EXT_4111, MIPS_AFL_EXT_4120): Likewise.
    	(MIPS_AFL_EXT_5400, MIPS_AFL_EXT_5500): Likewise.
    	(MIPS_AFL_EXT_LOONGSON_2E, MIPS_AFL_EXT_LOONGSON_2F): Likewise.
    	(Val_GNU_MIPS_ABI_FP_ANY, Val_GNU_MIPS_ABI_FP_DOUBLE): New enum values.
    	(Val_GNU_MIPS_ABI_FP_SINGLE, Val_GNU_MIPS_ABI_FP_SOFT): Likewise.
    	(Val_GNU_MIPS_ABI_FP_OLD_64, Val_GNU_MIPS_ABI_FP_XX): Likewise.
    	(Val_GNU_MIPS_ABI_FP_64, Val_GNU_MIPS_ABI_FP_64A): Likewise.
    	(Val_GNU_MIPS_ABI_FP_MAX): Likewise.
    	* sysdeps/mips/Makefile [subdir=elf]: Add tst-abi-interlink,
    	tst-mode-switch-1, tst-mode-switch-2, tst-mode-switch-3 tests.
    	* sysdeps/mips/bits/linkmap.h (struct link_map_machine): Add fpmode
    	field.
    	* sysdeps/mips/dl-machine.h (elf_machine_matches_host): Reject
    	EF_MIPS_FP64.
    	* sysdeps/mips/dl-machine-reject-phdr.h: New file.
    	* sysdeps/mips/tst-abi-fp32mod.c: Likewise.
    	* sysdeps/mips/tst-abi-fpxxmod.c: Likewise.
    	* sysdeps/mips/tst-abi-fpxxomod.c: Likewise.
    	* sysdeps/mips/tst-abi-fp64mod.c: Likewise.
    	* sysdeps/mips/tst-abi-fp64amod.c: Likewise.
    	* sysdeps/mips/tst-abi-interlink.c: Likewise.
    	* sysdeps/mips/tst-mode-switch-1.c: Likewise.
    	* sysdeps/mips/tst-mode-switch-2.c: Likewise.
    	* sysdeps/mips/tst-mode-switch-3.c: Likewise.
    	* sysdeps/unix/sysv/linux/mips/configure.ac (o32-fpabi): Define to
    	record the current FP ABI extension.
    	(mips-mode-switch): Define to show if kernel headers support mode
    	switching.
    	* sysdeps/unix/sysv/linux/mips/configure: Regenerate.
    	* sysdeps/unix/sysv/linux/mips/ldsodefs.h: Increase maximum
    	supported SYSV ABI version to 3.
    	* sysdeps/unix/sysv/linux/mips/libc-abis: Add new MIPS_O32_FP64
    	feature.

diff --git a/ChangeLog b/ChangeLog
index 3e2d4b0..d9d6790 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,53 @@
+2014-12-31  Matthew Fortune  <matthew.fortune@imgtec.com>
+
+	* elf/elf.h (PT_MIPS_ABIFLAGS): Define.
+	(Elf_MIPS_ABIFlags_v0): New structure.
+	(EF_MIPS_FP64): Define.
+	(MIPS_AFL_REG_NONE, MIPS_AFL_REG_32, MIPS_AFL_REG_64): Likewise.
+	(MIPS_AFL_REG_128, MIPS_AFL_ASE_DSP, MIPS_AFL_ASE_DSP64): Likewise.
+	(MIPS_AFL_ASE_DSPR2, MIPS_AFL_ASE_EVA, MIPS_AFL_ASE_MCU): Likewise.
+	(MIPS_AFL_ASE_MDMX, MIPS_AFL_ASE_MIPS3D, MIPS_AFL_ASE_MT): Likewise.
+	(MIPS_AFL_ASE_SMARTMIPS, MIPS_AFL_ASE_VIRT): Likewise.
+	(MIPS_AFL_ASE_VIRT64, MIPS_AFL_ASE_MSA, MIPS_AFL_ASE_MSA64): Likewise.
+	(MIPS_AFL_ASE_MIPS16, MIPS_AFL_ASE_MICROMIPS): Likewise.
+	(MIPS_AFL_ASE_XPA, MIPS_AFL_EXT_XLR, MIPS_AFL_EXT_OCTEON2): Likewise.
+	(MIPS_AFL_EXT_OCTEONP, MIPS_AFL_EXT_LOONGSON_3A): Likewise.
+	(MIPS_AFL_EXT_OCTEON, MIPS_AFL_EXT_5900, MIPS_AFL_EXT_4010): Likewise.
+	(MIPS_AFL_EXT_4100, MIPS_AFL_EXT_3900, MIPS_AFL_EXT_10000): Likewise.
+	(MIPS_AFL_EXT_SB1, MIPS_AFL_EXT_4111, MIPS_AFL_EXT_4120): Likewise.
+	(MIPS_AFL_EXT_5400, MIPS_AFL_EXT_5500): Likewise.
+	(MIPS_AFL_EXT_LOONGSON_2E, MIPS_AFL_EXT_LOONGSON_2F): Likewise.
+	(Val_GNU_MIPS_ABI_FP_ANY, Val_GNU_MIPS_ABI_FP_DOUBLE): New enum values.
+	(Val_GNU_MIPS_ABI_FP_SINGLE, Val_GNU_MIPS_ABI_FP_SOFT): Likewise.
+	(Val_GNU_MIPS_ABI_FP_OLD_64, Val_GNU_MIPS_ABI_FP_XX): Likewise.
+	(Val_GNU_MIPS_ABI_FP_64, Val_GNU_MIPS_ABI_FP_64A): Likewise.
+	(Val_GNU_MIPS_ABI_FP_MAX): Likewise.
+	* sysdeps/mips/Makefile [subdir=elf]: Add tst-abi-interlink,
+	tst-mode-switch-1, tst-mode-switch-2, tst-mode-switch-3 tests.
+	* sysdeps/mips/bits/linkmap.h (struct link_map_machine): Add fpmode
+	field.
+	* sysdeps/mips/dl-machine.h (elf_machine_matches_host): Reject
+	EF_MIPS_FP64.
+	* sysdeps/mips/dl-machine-reject-phdr.h: New file.
+	* sysdeps/mips/tst-abi-fp32mod.c: Likewise.
+	* sysdeps/mips/tst-abi-fpxxmod.c: Likewise.
+	* sysdeps/mips/tst-abi-fpxxomod.c: Likewise.
+	* sysdeps/mips/tst-abi-fp64mod.c: Likewise.
+	* sysdeps/mips/tst-abi-fp64amod.c: Likewise.
+	* sysdeps/mips/tst-abi-interlink.c: Likewise.
+	* sysdeps/mips/tst-mode-switch-1.c: Likewise.
+	* sysdeps/mips/tst-mode-switch-2.c: Likewise.
+	* sysdeps/mips/tst-mode-switch-3.c: Likewise.
+	* sysdeps/unix/sysv/linux/mips/configure.ac (o32-fpabi): Define to
+	record the current FP ABI extension.
+	(mips-mode-switch): Define to show if kernel headers support mode
+	switching.
+	* sysdeps/unix/sysv/linux/mips/configure: Regenerate.
+	* sysdeps/unix/sysv/linux/mips/ldsodefs.h: Increase maximum
+	supported SYSV ABI version to 3.
+	* sysdeps/unix/sysv/linux/mips/libc-abis: Add new MIPS_O32_FP64
+	feature.
+
 2014-12-31  Rajalakshmi Srinivasaraghavan  <raji@linux.vnet.ibm.com>
 	    Adhemerval Zanella  <azanella@linux.vnet.ibm.com>
 
diff --git a/elf/elf.h b/elf/elf.h
index 341cfa6..683224f 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1383,6 +1383,7 @@ typedef struct
 #define EF_MIPS_64BIT_WHIRL	16
 #define EF_MIPS_ABI2		32
 #define EF_MIPS_ABI_ON32	64
+#define EF_MIPS_FP64		512  /* Uses FP64 (12 callee-saved).  */
 #define EF_MIPS_NAN2008	1024  /* Uses IEEE 754-2008 NaN encoding.  */
 #define EF_MIPS_ARCH		0xf0000000 /* MIPS architecture level.  */
 
@@ -1631,9 +1632,10 @@ typedef struct
 
 /* Legal values for p_type field of Elf32_Phdr.  */
 
-#define PT_MIPS_REGINFO	0x70000000	/* Register usage information */
-#define PT_MIPS_RTPROC  0x70000001	/* Runtime procedure table. */
-#define PT_MIPS_OPTIONS 0x70000002
+#define PT_MIPS_REGINFO	  0x70000000	/* Register usage information. */
+#define PT_MIPS_RTPROC	  0x70000001	/* Runtime procedure table. */
+#define PT_MIPS_OPTIONS	  0x70000002
+#define PT_MIPS_ABIFLAGS  0x70000003	/* FP mode requirement. */
 
 /* Special program header types.  */
 
@@ -1755,6 +1757,101 @@ typedef struct
 
 typedef Elf32_Addr Elf32_Conflict;
 
+typedef struct
+{
+  /* Version of flags structure.  */
+  Elf32_Half version;
+  /* The level of the ISA: 1-5, 32, 64.  */
+  unsigned char isa_level;
+  /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise.  */
+  unsigned char isa_rev;
+  /* The size of general purpose registers.  */
+  unsigned char gpr_size;
+  /* The size of co-processor 1 registers.  */
+  unsigned char cpr1_size;
+  /* The size of co-processor 2 registers.  */
+  unsigned char cpr2_size;
+  /* The floating-point ABI.  */
+  unsigned char fp_abi;
+  /* Processor-specific extension.  */
+  Elf32_Word isa_ext;
+  /* Mask of ASEs used.  */
+  Elf32_Word ases;
+  /* Mask of general flags.  */
+  Elf32_Word flags1;
+  Elf32_Word flags2;
+} Elf_MIPS_ABIFlags_v0;
+
+/* Values for the register size bytes of an abi flags structure.  */
+
+#define MIPS_AFL_REG_NONE	0x00	 /* No registers.  */
+#define MIPS_AFL_REG_32		0x01	 /* 32-bit registers.  */
+#define MIPS_AFL_REG_64		0x02	 /* 64-bit registers.  */
+#define MIPS_AFL_REG_128	0x03	 /* 128-bit registers.  */
+
+/* Masks for the ases word of an ABI flags structure.  */
+
+#define MIPS_AFL_ASE_DSP	0x00000001 /* DSP ASE.  */
+#define MIPS_AFL_ASE_DSPR2	0x00000002 /* DSP R2 ASE.  */
+#define MIPS_AFL_ASE_EVA	0x00000004 /* Enhanced VA Scheme.  */
+#define MIPS_AFL_ASE_MCU	0x00000008 /* MCU (MicroController) ASE.  */
+#define MIPS_AFL_ASE_MDMX	0x00000010 /* MDMX ASE.  */
+#define MIPS_AFL_ASE_MIPS3D	0x00000020 /* MIPS-3D ASE.  */
+#define MIPS_AFL_ASE_MT		0x00000040 /* MT ASE.  */
+#define MIPS_AFL_ASE_SMARTMIPS	0x00000080 /* SmartMIPS ASE.  */
+#define MIPS_AFL_ASE_VIRT	0x00000100 /* VZ ASE.  */
+#define MIPS_AFL_ASE_MSA	0x00000200 /* MSA ASE.  */
+#define MIPS_AFL_ASE_MIPS16	0x00000400 /* MIPS16 ASE.  */
+#define MIPS_AFL_ASE_MICROMIPS	0x00000800 /* MICROMIPS ASE.  */
+#define MIPS_AFL_ASE_XPA	0x00001000 /* XPA ASE.  */
+#define MIPS_AFL_ASE_MASK	0x00001fff /* All ASEs.  */
+
+/* Values for the isa_ext word of an ABI flags structure.  */
+
+#define MIPS_AFL_EXT_XLR	  1   /* RMI Xlr instruction.  */
+#define MIPS_AFL_EXT_OCTEON2	  2   /* Cavium Networks Octeon2.  */
+#define MIPS_AFL_EXT_OCTEONP	  3   /* Cavium Networks OcteonP.  */
+#define MIPS_AFL_EXT_LOONGSON_3A  4   /* Loongson 3A.  */
+#define MIPS_AFL_EXT_OCTEON	  5   /* Cavium Networks Octeon.  */
+#define MIPS_AFL_EXT_5900	  6   /* MIPS R5900 instruction.  */
+#define MIPS_AFL_EXT_4650	  7   /* MIPS R4650 instruction.  */
+#define MIPS_AFL_EXT_4010	  8   /* LSI R4010 instruction.  */
+#define MIPS_AFL_EXT_4100	  9   /* NEC VR4100 instruction.  */
+#define MIPS_AFL_EXT_3900	  10  /* Toshiba R3900 instruction.  */
+#define MIPS_AFL_EXT_10000	  11  /* MIPS R10000 instruction.  */
+#define MIPS_AFL_EXT_SB1	  12  /* Broadcom SB-1 instruction.  */
+#define MIPS_AFL_EXT_4111	  13  /* NEC VR4111/VR4181 instruction.  */
+#define MIPS_AFL_EXT_4120	  14  /* NEC VR4120 instruction.  */
+#define MIPS_AFL_EXT_5400	  15  /* NEC VR5400 instruction.  */
+#define MIPS_AFL_EXT_5500	  16  /* NEC VR5500 instruction.  */
+#define MIPS_AFL_EXT_LOONGSON_2E  17  /* ST Microelectronics Loongson 2E.  */
+#define MIPS_AFL_EXT_LOONGSON_2F  18  /* ST Microelectronics Loongson 2F.  */
+
+/* Masks for the flags1 word of an ABI flags structure.  */
+#define MIPS_AFL_FLAGS1_ODDSPREG  1  /* Uses odd single-precision registers.  */
+
+/* Object attribute values.  */
+enum
+{
+  /* Not tagged or not using any ABIs affected by the differences.  */
+  Val_GNU_MIPS_ABI_FP_ANY = 0,
+  /* Using hard-float -mdouble-float.  */
+  Val_GNU_MIPS_ABI_FP_DOUBLE = 1,
+  /* Using hard-float -msingle-float.  */
+  Val_GNU_MIPS_ABI_FP_SINGLE = 2,
+  /* Using soft-float.  */
+  Val_GNU_MIPS_ABI_FP_SOFT = 3,
+  /* Using -mips32r2 -mfp64.  */
+  Val_GNU_MIPS_ABI_FP_OLD_64 = 4,
+  /* Using -mfpxx.  */
+  Val_GNU_MIPS_ABI_FP_XX = 5,
+  /* Using -mips32r2 -mfp64.  */
+  Val_GNU_MIPS_ABI_FP_64 = 6,
+  /* Using -mips32r2 -mfp64 -mno-odd-spreg.  */
+  Val_GNU_MIPS_ABI_FP_64A = 7,
+  /* Maximum allocated FP ABI value.  */
+  Val_GNU_MIPS_ABI_FP_MAX = 7
+};
 
 /* HPPA specific definitions.  */
 
diff --git a/sysdeps/mips/Makefile b/sysdeps/mips/Makefile
index a152699..463b121 100644
--- a/sysdeps/mips/Makefile
+++ b/sysdeps/mips/Makefile
@@ -26,3 +26,53 @@ CPPFLAGS-crtn.S += $(pic-ccflag)
 endif
 
 ASFLAGS-.os += $(pic-ccflag)
+
+ifeq ($(subdir),elf)
+ifneq ($(o32-fpabi),)
+tests += tst-abi-interlink
+
+fpabi-modules-names =
+fpabi_list =
+ifneq (,$(filter $(o32-fpabi),32 xx xxo))
+fpabi-modules-names += tst-abi-fp32mod
+CFLAGS-tst-abi-fp32mod.c += -mfp32
+endif
+ifneq (,$(filter $(o32-fpabi),xx))
+fpabi-modules-names += tst-abi-fpxxmod
+CFLAGS-tst-abi-fpxxmod.c += -mfpxx -mno-odd-spreg
+endif
+ifneq (,$(filter $(o32-fpabi),xx xxo))
+fpabi-modules-names += tst-abi-fpxxomod
+CFLAGS-tst-abi-fpxxomod.c += -mfpxx -modd-spreg
+endif
+ifneq (,$(filter $(o32-fpabi),xx 64a))
+fpabi-modules-names += tst-abi-fp64amod
+CFLAGS-tst-abi-fp64amod.c += -mfp64 -mno-odd-spreg
+endif
+ifneq (,$(filter $(o32-fpabi),xx xxo 64a 64))
+fpabi-modules-names += tst-abi-fp64mod
+CFLAGS-tst-abi-fp64mod.c += -mfp64 -modd-spreg
+endif
+modules-names += $(fpabi-modules-names)
+
+comma:=,
+empty:=
+space:=$(empty) $(empty)
+fpabi_list=$(subst $(space),$(comma),$(patsubst tst-abi-%mod,o_%,\
+				     $(fpabi-modules-names)))
+CPPFLAGS-tst-abi-interlink.c += -DFPABI_LIST=$(fpabi_list)
+CPPFLAGS-tst-abi-interlink.c += -DFPABI_COUNT=$(words $(fpabi-modules-names))
+CPPFLAGS-tst-abi-interlink.c += -DFPABI_NATIVE=o_fp$(o32-fpabi)
+$(objpfx)tst-abi-interlink: $(libdl)
+$(objpfx)tst-abi-interlink.out: $(patsubst %,$(objpfx)%.so,\
+					   $(fpabi-modules-names))
+endif
+
+ifeq ($(mips-mode-switch),yes)
+ifeq ($(o32-fpabi),xx)
+tests += tst-mode-switch-1 tst-mode-switch-2 tst-mode-switch-3
+$(objpfx)tst-mode-switch-1: $(shared-thread-library)
+$(objpfx)tst-mode-switch-2: $(shared-thread-library)
+endif
+endif
+endif
diff --git a/sysdeps/mips/bits/linkmap.h b/sysdeps/mips/bits/linkmap.h
index a6df782..1fb9678 100644
--- a/sysdeps/mips/bits/linkmap.h
+++ b/sysdeps/mips/bits/linkmap.h
@@ -1,4 +1,6 @@
 struct link_map_machine
   {
     ElfW(Addr) plt; /* Address of .plt */
+    ElfW(Word) fpabi; /* FP ABI of the object */
+    unsigned int odd_spreg; /* Does the object require odd_spreg support? */
   };
diff --git a/sysdeps/mips/dl-machine-reject-phdr.h b/sysdeps/mips/dl-machine-reject-phdr.h
new file mode 100644
index 0000000..b277450
--- /dev/null
+++ b/sysdeps/mips/dl-machine-reject-phdr.h
@@ -0,0 +1,326 @@
+/* Machine-dependent program header inspection for the ELF loader.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DL_MACHINE_REJECT_PHDR_H
+#define _DL_MACHINE_REJECT_PHDR_H 1
+
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE
+# define HAVE_PRCTL_FP_MODE 1
+#else
+# define HAVE_PRCTL_FP_MODE 0
+#endif
+
+/* Reject an object with a debug message.  */
+#define REJECT(str, args...)						      \
+  {									      \
+    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))		      \
+      _dl_debug_printf (str, ##args);					      \
+    return true;							      \
+  }
+
+/* Search the program headers for the ABI Flags.  */
+
+static inline const ElfW(Phdr) *
+find_mips_abiflags (const ElfW(Phdr) *phdr, ElfW(Half) phnum)
+{
+  const ElfW(Phdr) *ph;
+
+  for (ph = phdr; ph < &phdr[phnum]; ++ph)
+    if (ph->p_type == PT_MIPS_ABIFLAGS)
+      return ph;
+  return NULL;
+}
+
+/* Cache the FP ABI value from the PT_MIPS_ABIFLAGS program header.  */
+
+static bool
+cached_fpabi_reject_phdr_p (struct link_map *l)
+{
+  if (l->l_mach.fpabi == 0)
+    {
+      const ElfW(Phdr) *ph = find_mips_abiflags (l->l_phdr, l->l_phnum);
+
+      if (ph)
+	{
+	  Elf_MIPS_ABIFlags_v0 * mips_abiflags;
+	  if (ph->p_filesz < sizeof (Elf_MIPS_ABIFlags_v0))
+	    REJECT ("   %s: malformed PT_MIPS_ABIFLAGS found\n", l->l_name);
+
+	  mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (l->l_addr + ph->p_vaddr);
+
+	  if (__glibc_unlikely (mips_abiflags->flags2 != 0))
+	    REJECT ("   %s: unknown MIPS.abiflags flags2: %u\n", l->l_name,
+		    mips_abiflags->flags2);
+
+	  l->l_mach.fpabi = mips_abiflags->fp_abi;
+	  l->l_mach.odd_spreg = (mips_abiflags->flags1
+				 & MIPS_AFL_FLAGS1_ODDSPREG) != 0;
+	}
+      else
+	{
+	  l->l_mach.fpabi = -1;
+	  l->l_mach.odd_spreg = true;
+	}
+    }
+  return false;
+}
+
+/* Return a description of the specified floating-point ABI.  */
+
+static const char *
+fpabi_string (int fpabi)
+{
+  switch (fpabi)
+    {
+    case Val_GNU_MIPS_ABI_FP_ANY:
+      return "Hard or soft float";
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      return "Hard float (double precision)";
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      return "Hard float (single precision)";
+    case Val_GNU_MIPS_ABI_FP_SOFT:
+      return "Soft float";
+    case Val_GNU_MIPS_ABI_FP_OLD_64:
+      return "Unsupported FP64";
+    case Val_GNU_MIPS_ABI_FP_XX:
+      return "Hard float (32-bit CPU, Any FPU)";
+    case Val_GNU_MIPS_ABI_FP_64:
+      return "Hard float (32-bit CPU, 64-bit FPU)";
+    case Val_GNU_MIPS_ABI_FP_64A:
+      return "Hard float compat (32-bit CPU, 64-bit FPU)";
+    case -1:
+      return "Double precision, single precision or soft float";
+    default:
+      return "Unknown FP ABI";
+    }
+}
+
+/* A structure to describe the requirements of each FP ABI extension.
+   Each field says whether the ABI can be executed in that mode.  The FR0 field
+   is actually overloaded and means 'default' FR mode for the ABI.  I.e. For
+   O32 it is FR0 and for N32/N64 it is actually FR1.  Since this logic is
+   focussed on the intricacies of mode management for O32 we call the field
+   FR0.  */
+
+struct abi_req
+{
+  bool single;
+  bool soft;
+  bool fr0;
+  bool fr1;
+  bool fre;
+};
+
+/* FP ABI requirements for all Val_GNU_MIPS_ABI_FP_* values.  */
+
+static const struct abi_req reqs[Val_GNU_MIPS_ABI_FP_MAX + 1] =
+    {{true,  true,  true,  true,  true},  /* Any */
+     {false, false, true,  false, true},  /* Double-float */
+     {true,  false, false, false, false}, /* Single-float */
+     {false, true,  false, false, false}, /* Soft-float */
+     {false, false, false, false, false}, /* old-FP64 */
+     {false, false, true,  true,  true},  /* FPXX */
+     {false, false, false, true,  false}, /* FP64 */
+     {false, false, false, true,  true}}; /* FP64A */
+
+/* FP ABI requirements for objects without a PT_MIPS_ABIFLAGS segment.  */
+
+static const struct abi_req none_req = { true, true, true, false, true };
+
+/* Return true iff ELF program headers are incompatible with the running
+   host.  This verifies that floating-point ABIs are compatible and
+   re-configures the hardware mode if necessary.  This code handles both the
+   DT_NEEDED libraries and the dlopen'ed libraries.  It also accounts for the
+   impact of dlclose.  */
+
+static bool __attribute_used__
+elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
+			   const char *buf, size_t len, struct link_map *map,
+			   int fd)
+{
+  const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum);
+  struct link_map *l;
+  Lmid_t nsid;
+  int in_abi = -1;
+  struct abi_req in_req;
+  Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL;
+  bool perfect_match = false;
+#if _MIPS_SIM == _ABIO32
+  unsigned int cur_mode = -1;
+# if HAVE_PRCTL_FP_MODE
+  bool cannot_mode_switch = false;
+
+  /* Get the current hardware mode.  */
+  cur_mode = __prctl (PR_GET_FP_MODE);
+# endif
+#endif
+
+  /* Read the attributes section.  */
+  if (ph != NULL)
+    {
+      ElfW(Addr) size = ph->p_filesz;
+
+      if (ph->p_offset + size <= len)
+	mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (buf + ph->p_offset);
+      else
+	{
+	  mips_abiflags = alloca (size);
+	  __lseek (fd, ph->p_offset, SEEK_SET);
+	  if (__libc_read (fd, (void *) mips_abiflags, size) != size)
+	    REJECT ("   unable to read PT_MIPS_ABIFLAGS\n");
+	}
+
+      if (size < sizeof (Elf_MIPS_ABIFlags_v0))
+	REJECT ("   contains malformed PT_MIPS_ABIFLAGS\n");
+
+      if (__glibc_unlikely (mips_abiflags->flags2 != 0))
+	REJECT ("   unknown MIPS.abiflags flags2: %u\n", mips_abiflags->flags2);
+
+      in_abi = mips_abiflags->fp_abi;
+    }
+
+  /* ANY is compatible with anything.  */
+  perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY);
+
+  /* Unknown ABIs are rejected.  */
+  if (in_abi != -1 && in_abi > Val_GNU_MIPS_ABI_FP_MAX)
+    REJECT ("   uses unknown FP ABI: %u\n", in_abi);
+
+  /* Obtain the initial requirements.  */
+  in_req = (in_abi == -1) ? none_req : reqs[in_abi];
+
+  /* Check that the new requirement does not conflict with any currently
+     loaded object.  */
+  for (nsid = 0; nsid < DL_NNS; ++nsid)
+    for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+      {
+	struct abi_req existing_req;
+
+	if (cached_fpabi_reject_phdr_p (l))
+	  return true;
+
+#if _MIPS_SIM == _ABIO32
+	/* A special case arises for O32 FP64 and FP64A where the kernel
+	   pre-dates PT_MIPS_ABIFLAGS.  These ABIs will be blindly loaded even
+	   if the hardware mode is unavailable or disabled.  In this
+	   circumstance the prctl call to obtain the current mode will fail.
+	   Detect this situation here and reject everything.  This will
+	   effectively prevent dynamically linked applications from failing in
+	   unusual ways but there is nothing we can do to help static
+	   applications.  */
+	if ((l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64A
+	     || l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64)
+	    && cur_mode == -1)
+	  REJECT ("   found %s running in the wrong mode\n",
+		  fpabi_string (l->l_mach.fpabi));
+#endif
+
+	/* Found a perfect match, success.  */
+	perfect_match |= (in_abi == l->l_mach.fpabi);
+
+	/* Unknown ABIs are rejected.  */
+	if (l->l_mach.fpabi != -1 && l->l_mach.fpabi > Val_GNU_MIPS_ABI_FP_MAX)
+	  REJECT ("   found unknown FP ABI: %u\n", l->l_mach.fpabi);
+
+	existing_req = (l->l_mach.fpabi == -1 ? none_req
+			: reqs[l->l_mach.fpabi]);
+
+	/* Merge requirements.  */
+	in_req.soft &= existing_req.soft;
+	in_req.single &= existing_req.single;
+	in_req.fr0 &= existing_req.fr0;
+	in_req.fr1 &= existing_req.fr1;
+	in_req.fre &= existing_req.fre;
+
+	/* If there is at least one mode which is still usable then the new
+	   object can be loaded.  */
+	if (in_req.single || in_req.soft || in_req.fr1 || in_req.fr0
+	    || in_req.fre)
+	  {
+#if _MIPS_SIM == _ABIO32 && HAVE_PRCTL_FP_MODE
+	    /* Account for loaded ABIs which prohibit mode switching.  */
+	    if (l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_XX)
+	      cannot_mode_switch |= l->l_mach.odd_spreg;
+#endif
+	  }
+	else
+	  REJECT ("   uses %s, already loaded %s\n",
+		  fpabi_string (in_abi),
+		  fpabi_string (l->l_mach.fpabi));
+      }
+
+#if _MIPS_SIM == _ABIO32
+  /* At this point we know that the newly loaded object is compatible with all
+     existing objects but the hardware mode may not be correct.  */
+  if ((in_req.fr1 || in_req.fre || in_req.fr0)
+      && !perfect_match)
+    {
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+	_dl_debug_printf ("   needs %s%s mode\n", in_req.fr0 ? "FR0 or " : "",
+			  (in_req.fre && !in_req.fr1) ? "FRE" : "FR1");
+
+      /* If the PR_GET_FP_MODE is not supported then only FR0 is available.
+	 If the overall requirements cannot be met by FR0 then reject the
+	 object.  */
+      if (cur_mode == -1)
+	return !in_req.fr0;
+
+# if HAVE_PRCTL_FP_MODE
+      {
+	unsigned int fr1_mode = PR_FP_MODE_FR;
+
+	/* It is not possible to change the mode of a thread which may be
+	   executing FPXX code with odd-singles.  If an FPXX object with
+	   odd-singles is loaded then just check the current mode is OK. This
+	   can be either the FR1 mode or FR0 if the requirements are met by
+	   FR0.  */
+	if (cannot_mode_switch)
+	  return (!(in_req.fre && cur_mode == (PR_FP_MODE_FR | PR_FP_MODE_FRE))
+		  && !(in_req.fr1 && cur_mode == PR_FP_MODE_FR)
+		  && !(in_req.fr0 && cur_mode == 0));
+
+	/* If the overall requirements can be satisfied by FRE but not FR1 then
+	   fr1_mode must become FRE.  */
+	if (in_req.fre && !in_req.fr1)
+	  fr1_mode |= PR_FP_MODE_FRE;
+
+	/* Set the new mode.  Use fr1_mode if the requirements cannot be met by
+	   FR0.  */
+	if (!in_req.fr0)
+	  return __prctl (PR_SET_FP_MODE, fr1_mode) != 0;
+	else if (__prctl (PR_SET_FP_MODE, /* fr0_mode */ 0) != 0)
+	  {
+	    /* Setting FR0 can validly fail on an R6 core so retry with the FR1
+	       mode as a fall back.  */
+	    if (errno != ENOTSUP)
+	      return true;
+
+	    return __prctl (PR_SET_FP_MODE, fr1_mode) != 0;
+	  }
+      }
+# endif /* HAVE_PRCTL_FP_MODE */
+    }
+#endif /* _MIPS_SIM == _ABIO32 */
+
+  return false;
+}
+
+#endif /* dl-machine-reject-phdr.h */
diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h
index 5000d2a..b463232 100644
--- a/sysdeps/mips/dl-machine.h
+++ b/sysdeps/mips/dl-machine.h
@@ -99,6 +99,11 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
   if ((ehdr->e_flags & EF_MIPS_NAN2008) != ELF_MACHINE_NAN2008)
     return 0;
 
+  /* Ensure that the old O32 FP64 ABI is never loaded, it is not supported
+     on linux.  */
+  if (ehdr->e_flags & EF_MIPS_FP64)
+    return 0;
+
   switch (ehdr->e_machine)
     {
     case EM_MIPS:
diff --git a/sysdeps/mips/tst-abi-fp32mod.c b/sysdeps/mips/tst-abi-fp32mod.c
new file mode 100644
index 0000000..89ff053
--- /dev/null
+++ b/sysdeps/mips/tst-abi-fp32mod.c
@@ -0,0 +1,22 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+int
+fp32 (void)
+{
+  return 1;
+}
diff --git a/sysdeps/mips/tst-abi-fp64amod.c b/sysdeps/mips/tst-abi-fp64amod.c
new file mode 100644
index 0000000..be3d781
--- /dev/null
+++ b/sysdeps/mips/tst-abi-fp64amod.c
@@ -0,0 +1,22 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+int
+fp64a (void)
+{
+  return 7;
+}
diff --git a/sysdeps/mips/tst-abi-fp64mod.c b/sysdeps/mips/tst-abi-fp64mod.c
new file mode 100644
index 0000000..04b5c36
--- /dev/null
+++ b/sysdeps/mips/tst-abi-fp64mod.c
@@ -0,0 +1,22 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+int
+fp64 (void)
+{
+  return 6;
+}
diff --git a/sysdeps/mips/tst-abi-fpxxmod.c b/sysdeps/mips/tst-abi-fpxxmod.c
new file mode 100644
index 0000000..69f73a1
--- /dev/null
+++ b/sysdeps/mips/tst-abi-fpxxmod.c
@@ -0,0 +1,22 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+int
+fpxx (void)
+{
+  return 5;
+}
diff --git a/sysdeps/mips/tst-abi-fpxxomod.c b/sysdeps/mips/tst-abi-fpxxomod.c
new file mode 100644
index 0000000..795973b
--- /dev/null
+++ b/sysdeps/mips/tst-abi-fpxxomod.c
@@ -0,0 +1,22 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+int
+fpxxo (void)
+{
+  return 5 + 100;
+}
diff --git a/sysdeps/mips/tst-abi-interlink.c b/sysdeps/mips/tst-abi-interlink.c
new file mode 100644
index 0000000..c9a29cc
--- /dev/null
+++ b/sysdeps/mips/tst-abi-interlink.c
@@ -0,0 +1,844 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/prctl.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE
+# define HAVE_PRCTL_FP_MODE 1
+# define FR1_MODE (PR_FP_MODE_FR)
+# define FRE_MODE (PR_FP_MODE_FR | PR_FP_MODE_FRE)
+#else
+# define HAVE_PRCTL_FP_MODE 0
+# define FR1_MODE 0x1
+# define FRE_MODE 0x2
+#endif
+
+#define STR_VAL(VAL) #VAL
+#define N_STR(VAL) STR_VAL(VAL)
+
+#define START_STATE(NAME) 					\
+case s_ ## NAME: 						\
+  {								\
+    switch (obj) 						\
+      {
+
+#define END_STATE						\
+      default:							\
+        return false;						\
+      }								\
+  break;							\
+  }
+
+#define NEXT(OBJ, NEXT_STATE)					\
+case o_ ## OBJ: 						\
+  current_fp_state = s_ ## NEXT_STATE;				\
+  break;
+
+#define NEXT_REQ_FR1(OBJ, NEXT_STATE)				\
+case o_ ## OBJ:							\
+  {								\
+    if (has_fr1)						\
+      current_fp_state = s_ ## NEXT_STATE;			\
+    else							\
+      return false;						\
+  }								\
+  break;
+
+#define NEXT_REQ_FR0(OBJ, NEXT_STATE) 				\
+case o_ ## OBJ:							\
+  {								\
+    if (!is_r6							\
+        || (is_r6 && has_fr1 && has_fre))			\
+      current_fp_state = s_ ## NEXT_STATE;			\
+    else 							\
+      return false;						\
+  }								\
+  break;
+
+#define NEXT_REQ_FRE(OBJ, NEXT_STATE)				\
+case o_ ## OBJ: 						\
+  {								\
+    if (has_fr1 && has_fre)					\
+      current_fp_state = s_ ## NEXT_STATE;			\
+    else							\
+      return false;						\
+  }								\
+  break;
+
+#define NEXT_NO_MODE_CHANGE(OBJ, NEXT_STATE)			\
+case o_ ## OBJ: 						\
+  {								\
+    if (current_mode_valid_p (s_ ## NEXT_STATE))			\
+      {								\
+	current_fp_state = s_ ## NEXT_STATE;			\
+	cant_change_mode = true;				\
+      }								\
+    else							\
+      return false;						\
+  }								\
+  break;
+
+static const char * const shared_lib_names[] =
+  {
+    "tst-abi-fpanymod.so", "tst-abi-fpsoftmod.so", "tst-abi-fpsinglemod.so",
+    "tst-abi-fp32mod.so", "tst-abi-fp64mod.so", "tst-abi-fp64amod.so",
+    "tst-abi-fpxxmod.so", "tst-abi-fpxxomod.so"
+  };
+
+struct fp_mode_req
+{
+  int mode1;
+  int mode2;
+  int mode3;
+};
+
+enum fp_obj
+{
+  o_any,
+  o_soft,
+  o_single,
+  o_fp32,
+  o_fp64,
+  o_fp64a,
+  o_fpxx,
+  o_fpxxo,
+  o_max
+};
+
+enum fp_state
+{
+  s_any,
+  s_soft,
+  s_single,
+  s_fp32,
+  s_fpxx,
+  s_fpxxo,
+  s_fp64a,
+  s_fp64,
+  s_fpxxo_fpxx,
+  s_fp32_fpxx,
+  s_fp32_fpxxo,
+  s_fp32_fpxxo_fpxx,
+  s_fp32_fp64a_fpxx,
+  s_fp32_fp64a_fpxxo,
+  s_fp32_fp64a_fpxxo_fpxx,
+  s_fp64a_fp32,
+  s_fp64a_fpxx,
+  s_fp64a_fpxxo,
+  s_fp64a_fp64,
+  s_fp64a_fp64_fpxx,
+  s_fp64a_fp64_fpxxo,
+  s_fp64a_fpxx_fpxxo,
+  s_fp64a_fp64_fpxxo_fpxx,
+  s_fp64_fpxx,
+  s_fp64_fpxxo,
+  s_fp64_fpxx_fpxxo
+};
+
+
+static int current_fp_mode;
+static bool cant_change_mode = false;
+static bool has_fr1 = false;
+static bool has_fre = false;
+static bool is_r6 = false;
+static unsigned int fp_obj_count[o_max];
+void * shared_lib_ptrs[o_max];
+static enum fp_state current_fp_state = s_any;
+static enum fp_obj test_objects[FPABI_COUNT] = { FPABI_LIST };
+
+/* This function will return the valid FP modes for the specified state.  */
+
+static struct fp_mode_req
+compute_fp_modes (enum fp_state state)
+{
+  struct fp_mode_req requirements;
+
+  requirements.mode1 = -1;
+  requirements.mode2 = -1;
+  requirements.mode3 = -1;
+
+  switch (state)
+    {
+    case s_single:
+      {
+        if (is_r6)
+	  requirements.mode1 = FR1_MODE;
+	else
+	  {
+	    requirements.mode1 = 0;
+	    requirements.mode2 = FR1_MODE;
+	  }
+	break;
+      }
+    case s_fp32:
+    case s_fp32_fpxx:
+    case s_fp32_fpxxo:
+    case s_fp32_fpxxo_fpxx:
+      {
+	if (is_r6)
+	  requirements.mode1 = FRE_MODE;
+	else
+	  {
+	    requirements.mode1 = 0;
+	    requirements.mode2 = FRE_MODE;
+	  }
+	break;
+      }
+    case s_fpxx:
+    case s_fpxxo:
+    case s_fpxxo_fpxx:
+    case s_any:
+    case s_soft:
+      {
+	if (is_r6)
+	  {
+	    requirements.mode1 = FR1_MODE;
+	    requirements.mode2 = FRE_MODE;
+	  }
+	else
+	  {
+	    requirements.mode1 = 0;
+	    requirements.mode2 = FR1_MODE;
+	    requirements.mode3 = FRE_MODE;
+	  }
+	break;
+      }
+    case s_fp64a:
+    case s_fp64a_fpxx:
+    case s_fp64a_fpxxo:
+    case s_fp64a_fpxx_fpxxo:
+      {
+	requirements.mode1 = FR1_MODE;
+	requirements.mode2 = FRE_MODE;
+	break;
+      }
+    case s_fp64:
+    case s_fp64_fpxx:
+    case s_fp64_fpxxo:
+    case s_fp64_fpxx_fpxxo:
+    case s_fp64a_fp64:
+    case s_fp64a_fp64_fpxx:
+    case s_fp64a_fp64_fpxxo:
+    case s_fp64a_fp64_fpxxo_fpxx:
+      {
+	requirements.mode1 = FR1_MODE;
+	break;
+      }
+    case s_fp64a_fp32:
+    case s_fp32_fp64a_fpxx:
+    case s_fp32_fp64a_fpxxo:
+    case s_fp32_fp64a_fpxxo_fpxx:
+      {
+        requirements.mode1 = FRE_MODE;
+        break;
+      }
+    }
+  return requirements;
+}
+
+/* Check the current mode is suitable for the specified state.  */
+
+static bool
+current_mode_valid_p (enum fp_state s)
+{
+  struct fp_mode_req req = compute_fp_modes (s);
+  return (req.mode1 == current_fp_mode
+	  || req.mode2 == current_fp_mode
+	  || req.mode3 == current_fp_mode);
+}
+
+/* Run the state machine by adding a new object.  */
+
+static bool
+set_next_fp_state (enum fp_obj obj)
+{
+  cant_change_mode = false;
+  switch (current_fp_state)
+    {
+
+    START_STATE(soft)
+    NEXT(soft,soft)
+    NEXT(any,soft)
+    END_STATE
+
+    START_STATE(single)
+    NEXT(single,single)
+    NEXT(any,single)
+    END_STATE
+
+    START_STATE(any)
+    NEXT_REQ_FR0(fp32, fp32)
+    NEXT(fpxx, fpxx)
+    NEXT(fpxxo, fpxxo)
+    NEXT_REQ_FR1(fp64a, fp64a)
+    NEXT_REQ_FR1(fp64, fp64)
+    NEXT(any,any)
+    NEXT(soft,soft)
+    NEXT(single,single)
+    END_STATE
+
+    START_STATE(fp32)
+    NEXT_REQ_FR0(fp32,fp32)
+    NEXT(fpxx, fp32_fpxx)
+    NEXT(fpxxo, fp32_fpxxo)
+    NEXT_REQ_FRE(fp64a, fp64a_fp32)
+    NEXT(any,fp32)
+    END_STATE
+
+    START_STATE(fpxx)
+    NEXT_REQ_FR0(fp32, fp32_fpxx)
+    NEXT_REQ_FR1(fp64, fp64_fpxx)
+    NEXT_REQ_FR1(fp64a, fp64a_fpxx)
+    NEXT(fpxxo, fpxxo_fpxx)
+    NEXT(fpxx,fpxx)
+    NEXT(any,fpxx)
+    END_STATE
+
+    START_STATE(fpxxo)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64, fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo,fpxxo)
+    NEXT_NO_MODE_CHANGE(any,fpxxo)
+    END_STATE
+
+    START_STATE(fp64a)
+    NEXT_REQ_FRE(fp32, fp64a_fp32)
+    NEXT_REQ_FR1(fp64, fp64a_fp64)
+    NEXT(fpxxo, fp64a_fpxxo)
+    NEXT(fpxx, fp64a_fpxx)
+    NEXT_REQ_FR1(fp64a, fp64a)
+    NEXT(any, fp64a)
+    END_STATE
+
+    START_STATE(fp64)
+    NEXT_REQ_FR1(fp64a, fp64a_fp64)
+    NEXT(fpxxo, fp64_fpxxo)
+    NEXT(fpxx, fp64_fpxx)
+    NEXT_REQ_FR1(fp64, fp64)
+    NEXT(any, fp64)
+    END_STATE
+
+    START_STATE(fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64, fp64_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(any, fpxxo_fpxx)
+    END_STATE
+
+    START_STATE(fp32_fpxx)
+    NEXT_REQ_FR0(fp32, fp32_fpxx)
+    NEXT(fpxx, fp32_fpxx)
+    NEXT(fpxxo, fp32_fpxxo_fpxx)
+    NEXT_REQ_FRE(fp64a, fp32_fp64a_fpxx)
+    NEXT(any, fp32_fpxx)
+    END_STATE
+
+    START_STATE(fp32_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp32_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp32_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(any, fp32_fpxxo)
+    END_STATE
+
+    START_STATE(fp32_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp32_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxx, fp32_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(any, fp32_fpxxo_fpxx)
+    END_STATE
+
+    START_STATE(fp64a_fp32)
+    NEXT_REQ_FRE(fp32, fp64a_fp32)
+    NEXT_REQ_FRE(fp64a, fp64a_fp32)
+    NEXT(fpxxo, fp32_fp64a_fpxxo)
+    NEXT(fpxx, fp32_fp64a_fpxx)
+    NEXT(any, fp64a_fp32)
+    END_STATE
+
+    START_STATE(fp64a_fpxx)
+    NEXT_REQ_FRE(fp32, fp32_fp64a_fpxx)
+    NEXT_REQ_FR1(fp64a, fp64a_fpxx)
+    NEXT(fpxx, fp64a_fpxx)
+    NEXT(fpxxo, fp64a_fpxx_fpxxo)
+    NEXT_REQ_FR1(fp64, fp64a_fp64_fpxx)
+    NEXT(any, fp64a_fpxx)
+    END_STATE
+
+    START_STATE(fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp64a_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(any, fp64a_fpxxo)
+    END_STATE
+
+    START_STATE(fp64a_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp64a_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(any, fp64a_fpxx_fpxxo)
+    END_STATE
+
+    START_STATE(fp64_fpxx)
+    NEXT_REQ_FR1(fp64a, fp64a_fp64_fpxx)
+    NEXT(fpxxo, fp64_fpxx_fpxxo)
+    NEXT(fpxx, fp64_fpxx)
+    NEXT_REQ_FR1(fp64, fp64_fpxx)
+    NEXT(any, fp64_fpxx)
+    END_STATE
+
+    START_STATE(fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp64_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64, fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(any, fp64_fpxxo)
+    END_STATE
+
+    START_STATE(fp64_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp64_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp64_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64, fp64_fpxx_fpxxo)
+    NEXT_NO_MODE_CHANGE(any, fp64_fpxx_fpxxo)
+    END_STATE
+
+    START_STATE(fp64a_fp64)
+    NEXT_REQ_FR1(fp64a, fp64a_fp64)
+    NEXT(fpxxo, fp64a_fp64_fpxxo)
+    NEXT(fpxx, fp64a_fp64_fpxx)
+    NEXT_REQ_FR1(fp64, fp64a_fp64)
+    NEXT(any, fp64a_fp64)
+    END_STATE
+
+    START_STATE(fp64a_fp64_fpxx)
+    NEXT_REQ_FR1(fp64a, fp64a_fp64_fpxx)
+    NEXT(fpxxo, fp64a_fp64_fpxxo_fpxx)
+    NEXT(fpxx, fp64a_fp64_fpxx)
+    NEXT_REQ_FR1(fp64, fp64a_fp64_fpxx)
+    NEXT(any, fp64a_fp64_fpxx)
+    END_STATE
+
+    START_STATE(fp64a_fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo)
+    NEXT_NO_MODE_CHANGE(any, fp64a_fp64_fpxxo)
+    END_STATE
+
+    START_STATE(fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxx, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(any, fp64a_fp64_fpxxo_fpxx)
+    END_STATE
+
+    START_STATE(fp32_fp64a_fpxx)
+    NEXT_REQ_FRE(fp32, fp32_fp64a_fpxx)
+    NEXT_REQ_FRE(fp64a, fp32_fp64a_fpxx)
+    NEXT(fpxxo, fp32_fp64a_fpxxo_fpxx)
+    NEXT(fpxx, fp32_fp64a_fpxx)
+    NEXT(any, fp32_fp64a_fpxx)
+    END_STATE
+
+    START_STATE(fp32_fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(fpxx, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp32_fp64a_fpxxo)
+    NEXT_NO_MODE_CHANGE(any, fp32_fp64a_fpxxo)
+    END_STATE
+
+    START_STATE(fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxx, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(fpxxo, fp32_fp64a_fpxxo_fpxx)
+    NEXT_NO_MODE_CHANGE(any, fp32_fp64a_fpxxo_fpxx)
+    END_STATE
+    }
+
+  if (obj != o_max)
+    fp_obj_count[obj]++;
+
+  return true;
+}
+
+/* Run the state machine by removing an object.  */
+
+static bool
+remove_object (enum fp_obj obj)
+{
+  if (obj == o_max)
+    return false;
+
+  fp_obj_count[obj]--;
+
+  /* We can't change fp state until all the objects
+     of a particular type have been unloaded.  */
+  if (fp_obj_count[obj] != 0)
+    return false;
+
+  switch (current_fp_state)
+    {
+    START_STATE(soft)
+    NEXT(soft,any)
+    END_STATE
+
+    START_STATE(single)
+    NEXT(single,any)
+    END_STATE
+
+    START_STATE(any)
+    NEXT(any,any)
+    END_STATE
+
+    START_STATE(fp32)
+    NEXT (fp32,any)
+    END_STATE
+
+    START_STATE(fpxx)
+    NEXT (fpxx,any)
+    END_STATE
+
+    START_STATE(fpxxo)
+    NEXT (fpxxo,any)
+    END_STATE
+
+    START_STATE(fp64a)
+    NEXT(fp64a, any)
+    END_STATE
+
+    START_STATE(fp64)
+    NEXT(fp64, any)
+    END_STATE
+
+    START_STATE(fpxxo_fpxx)
+    NEXT(fpxx, fpxxo)
+    NEXT(fpxxo, fpxx)
+    END_STATE
+
+    START_STATE(fp32_fpxx)
+    NEXT(fp32, fpxx)
+    NEXT(fpxx, fp32)
+    END_STATE
+
+    START_STATE(fp32_fpxxo)
+    NEXT(fp32, fpxxo)
+    NEXT(fpxxo, fp32)
+    END_STATE
+
+    START_STATE(fp32_fpxxo_fpxx)
+    NEXT(fp32, fpxxo_fpxx)
+    NEXT(fpxxo, fp32_fpxx)
+    NEXT(fpxx, fp32_fpxxo)
+    END_STATE
+
+    START_STATE(fp64a_fp32)
+    NEXT(fp32, fp64a)
+    NEXT(fp64a, fp32)
+    END_STATE
+
+    START_STATE(fp64a_fpxx)
+    NEXT(fp64a, fpxx)
+    NEXT(fpxx, fp64a)
+    END_STATE
+
+    START_STATE(fp64a_fpxxo)
+    NEXT(fp64a, fpxxo)
+    NEXT(fpxxo, fp64a)
+    END_STATE
+
+    START_STATE(fp64a_fpxx_fpxxo)
+    NEXT(fp64a, fpxxo_fpxx)
+    NEXT(fpxx, fp64a_fpxxo)
+    NEXT(fpxxo, fp64a_fpxx)
+    END_STATE
+
+    START_STATE(fp64_fpxx)
+    NEXT(fpxx, fp64)
+    NEXT(fp64, fpxx)
+    END_STATE
+
+    START_STATE(fp64_fpxxo)
+    NEXT(fpxxo, fp64)
+    NEXT(fp64, fpxxo)
+    END_STATE
+
+    START_STATE(fp64_fpxx_fpxxo)
+    NEXT(fp64, fpxxo_fpxx)
+    NEXT(fpxxo, fp64_fpxx)
+    NEXT(fpxx, fp64_fpxxo)
+    END_STATE
+
+    START_STATE(fp64a_fp64)
+    NEXT(fp64a, fp64)
+    NEXT(fp64, fp64a)
+    END_STATE
+
+    START_STATE(fp64a_fp64_fpxx)
+    NEXT(fp64a, fp64_fpxx)
+    NEXT(fpxx, fp64a_fp64)
+    NEXT(fp64, fp64a_fpxx)
+    END_STATE
+
+    START_STATE(fp64a_fp64_fpxxo)
+    NEXT(fp64a, fp64_fpxxo)
+    NEXT(fpxxo, fp64a_fp64)
+    NEXT(fp64, fp64a_fpxxo)
+    END_STATE
+
+    START_STATE(fp64a_fp64_fpxxo_fpxx)
+    NEXT(fp64a, fp64_fpxx_fpxxo)
+    NEXT(fpxx, fp64a_fp64_fpxxo)
+    NEXT(fpxxo, fp64a_fp64_fpxx)
+    NEXT(fp64, fp64a_fpxx_fpxxo)
+    END_STATE
+
+    START_STATE(fp32_fp64a_fpxx)
+    NEXT(fp32, fp64a_fpxx)
+    NEXT(fp64a, fp32_fpxx)
+    NEXT(fpxx, fp64a_fp32)
+    END_STATE
+
+    START_STATE(fp32_fp64a_fpxxo)
+    NEXT(fp32, fp64a_fpxxo)
+    NEXT(fp64a, fp32_fpxxo)
+    NEXT(fpxxo, fp64a_fp32)
+    END_STATE
+
+    START_STATE(fp32_fp64a_fpxxo_fpxx)
+    NEXT(fp32, fp64a_fpxx_fpxxo)
+    NEXT(fp64a, fp32_fpxxo_fpxx)
+    NEXT(fpxx, fp32_fp64a_fpxxo)
+    NEXT(fpxxo, fp32_fp64a_fpxx)
+    END_STATE
+    }
+
+  return true;
+}
+
+static int
+mode_transition_valid_p (void)
+{
+  int prev_fp_mode;
+
+  /* Get the current fp mode.  */
+  prev_fp_mode = current_fp_mode;
+#if HAVE_PRCTL_FP_MODE
+  current_fp_mode = prctl (PR_GET_FP_MODE);
+
+  /* If the prctl call fails assume the core only has FR0 mode support.  */
+  if (current_fp_mode == -1)
+    current_fp_mode = 0;
+#endif
+
+  if (!current_mode_valid_p (current_fp_state))
+    return 0;
+
+  /* Check if mode changes are not allowed but a mode change happened.  */
+  if (cant_change_mode
+      && current_fp_mode != prev_fp_mode)
+    return 0;
+
+  return 1;
+}
+
+/* Load OBJ and check that it was/was not loaded correctly.  */
+bool
+load_object (enum fp_obj obj)
+{
+  bool should_load = set_next_fp_state (obj);
+
+  shared_lib_ptrs[obj] = dlopen (shared_lib_names[obj], RTLD_LAZY);
+
+  /* If we expected an error and the load was successful then fail.  */
+  if (!should_load && (shared_lib_ptrs[obj] != 0))
+    return false;
+
+  if (should_load && (shared_lib_ptrs[obj] == 0))
+    return false;
+
+  if (!mode_transition_valid_p ())
+    return false;
+
+  return true;
+}
+
+/* Remove an object and check the state remains valid.  */
+bool
+unload_object (enum fp_obj obj)
+{
+  if (!shared_lib_ptrs[obj])
+    return true;
+
+  remove_object (obj);
+
+  if (dlclose (shared_lib_ptrs[obj]) != 0)
+    return false;
+
+  shared_lib_ptrs[obj] = 0;
+
+  if (!mode_transition_valid_p ())
+    return false;
+
+  return true;
+}
+
+/* Load every permuation of OBJECTS.  */
+static bool
+test_permutations (enum fp_obj objects[], int count)
+{
+  int i;
+
+  for (i = 0 ; i < count ; i++)
+    {
+      if (!load_object (objects[i]))
+	return false;
+
+      if (count > 1)
+	{
+	  enum fp_obj new_objects[count - 1];
+	  int j;
+	  int k = 0;
+
+	  for (j = 0 ; j < count ; j++)
+	    {
+	      if (j != i)
+		new_objects[k++] = objects[j];
+	    }
+
+	  if (!test_permutations (new_objects, count - 1))
+	    return false;
+	}
+
+      if (!unload_object (objects[i]))
+	return false;
+    }
+  return true;
+}
+
+int
+do_test (void)
+{
+#if HAVE_PRCTL_FP_MODE
+  /* Determine available hardware support and current mode.  */
+  current_fp_mode = prctl (PR_GET_FP_MODE);
+
+  /* If the prctl call fails assume the core only has FR0 mode support.  */
+  if (current_fp_mode == -1)
+    current_fp_mode = 0;
+  else
+    {
+      if (prctl (PR_SET_FP_MODE, 0) != 0)
+	{
+	  if (errno == ENOTSUP)
+	    is_r6 = true;
+	  else
+	    {
+	      printf ("unexpected error from PR_SET_FP_MODE, 0: %m\n");
+	      return 1;
+	    }
+	}
+
+      if (prctl (PR_SET_FP_MODE, PR_FP_MODE_FR) != 0)
+	{
+	  if (errno != ENOTSUP)
+	    {
+	      printf ("unexpected error from PR_SET_FP_MODE, "
+		      "PR_FP_MODE_FR: %m\n");
+	      return 1;
+	    }
+	}
+      else
+	has_fr1 = true;
+
+      if (prctl (PR_SET_FP_MODE, PR_FP_MODE_FR | PR_FP_MODE_FRE) != 0)
+	{
+	  if (errno != ENOTSUP)
+	    {
+	      printf ("unexpected error from PR_SET_FP_MODE, "
+		      "PR_FP_MODE_FR | PR_FP_MODE_FRE: %m\n");
+	      return 1;
+	    }
+	}
+      else
+	has_fre = true;
+
+      if (prctl (PR_SET_FP_MODE, current_fp_mode) != 0)
+	{
+	  printf ("unable to restore initial FP mode: %m\n");
+	  return 1;
+	}
+    }
+
+  if ((is_r6 && !(current_fp_mode & PR_FP_MODE_FR))
+      || (!has_fr1 && (current_fp_mode & PR_FP_MODE_FR))
+      || (!has_fre && (current_fp_mode & PR_FP_MODE_FRE)))
+    {
+      puts ("Inconsistency detected between initial FP mode "
+	    "and supported FP modes\n");
+      return 1;
+    }
+#else
+  current_fp_mode = 0;
+#endif
+
+  /* Set up the initial state from executable and LDSO.  Assumptions:
+     1) All system libraries have the same ABI as ld.so.
+     2) Due to the fact that ld.so is tested by invoking it directly
+        rather than via an interpreter, there is no point in varying
+	the ABI of the test program.  Instead the ABI only varies for
+	the shared libraries which get loaded.  */
+  if (!set_next_fp_state (FPABI_NATIVE))
+    {
+      puts ("Unable to enter initial ABI state\n");
+      return 1;
+    }
+
+  /* Compare the computed state with the hardware state.  */
+  if (!mode_transition_valid_p ())
+    return 1;
+
+  /* Run all possible test permutations.  */
+  if (!test_permutations (test_objects, FPABI_COUNT))
+    {
+      puts ("Mode checks failed\n");
+      return 1;
+    }
+
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../../test-skeleton.c"
diff --git a/sysdeps/mips/tst-mode-switch-1.c b/sysdeps/mips/tst-mode-switch-1.c
new file mode 100644
index 0000000..eb8d68a
--- /dev/null
+++ b/sysdeps/mips/tst-mode-switch-1.c
@@ -0,0 +1,123 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/prctl.h>
+
+#if __mips_fpr != 0 || _MIPS_SPFPSET != 16
+# error This test requires -mfpxx -mno-odd-spreg
+#endif
+
+/* This test verifies that mode changes do not clobber register state
+   in other threads.  */
+
+static volatile int finished;
+static int mode[6] =
+  {
+    0,
+    PR_FP_MODE_FR,
+    PR_FP_MODE_FR | PR_FP_MODE_FRE,
+    PR_FP_MODE_FR,
+    0,
+    PR_FP_MODE_FR | PR_FP_MODE_FRE
+  };
+
+static void *
+thread_function (void * arg __attribute__ ((unused)))
+{
+  volatile int i = 0;
+  volatile float f = 0.0;
+  volatile double d = 0.0;
+
+  while (!finished)
+    {
+      if ((float) i != f || (double) i != d)
+	{
+	  printf ("unexpected value: i(%d) f(%f) d(%f)\n", i, f, d);
+	  exit (1);
+	}
+
+      if (i == 100)
+	{
+	  i = 0;
+	  f = 0.0;
+	  d = 0.0;
+	}
+
+      i++;
+      f++;
+      d++;
+    }
+  return NULL;
+}
+
+int
+main (void)
+{
+  int count = sysconf (_SC_NPROCESSORS_ONLN);
+  if (count <= 0)
+    count = 1;
+  count *= 4;
+
+  pthread_t th[count];
+  int i;
+  int result = 0;
+
+  for (i = 0; i < count; ++i)
+    if (pthread_create (&th[i], NULL, thread_function, 0) != 0)
+      {
+	printf ("creation of thread %d failed\n", i);
+	exit (1);
+      }
+
+  for (i = 0 ; i < 1000000 ; i++)
+    {
+      if (prctl (PR_SET_FP_MODE, mode[i % 6]) != 0
+	  && errno != ENOTSUP)
+	{
+	  printf ("prctl PR_SET_FP_MODE failed: %m\n");
+	  exit (1);
+	}
+    }
+
+  finished = 1;
+
+  for (i = 0; i < count; ++i)
+    {
+      void *v;
+      if (pthread_join (th[i], &v) != 0)
+	{
+	  printf ("join of thread %d failed\n", i);
+	  result = 1;
+	}
+      else if (v != NULL)
+	{
+	  printf ("join %d successful, but child failed\n", i);
+	  result = 1;
+	}
+      else
+	printf ("join %d successful\n", i);
+    }
+
+  return result;
+}
diff --git a/sysdeps/mips/tst-mode-switch-2.c b/sysdeps/mips/tst-mode-switch-2.c
new file mode 100644
index 0000000..c328896
--- /dev/null
+++ b/sysdeps/mips/tst-mode-switch-2.c
@@ -0,0 +1,163 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/prctl.h>
+
+#if __mips_fpr != 0 || _MIPS_SPFPSET != 16
+# error This test requires -mfpxx -mno-odd-spreg
+#endif
+
+/* This test verifies that all threads in a process see a mode
+   change when any thread causes a mode change.  */
+
+static int mode[6] =
+  {
+    0,
+    PR_FP_MODE_FR,
+    PR_FP_MODE_FR | PR_FP_MODE_FRE,
+    PR_FP_MODE_FR,
+    0,
+    PR_FP_MODE_FR | PR_FP_MODE_FRE
+  };
+static volatile int current_mode;
+static volatile int finished;
+static pthread_barrier_t barr_ready;
+static pthread_barrier_t barr_cont;
+
+static void *
+thread_function (void * arg __attribute__ ((unused)))
+{
+  while (!finished)
+    {
+      int res = pthread_barrier_wait (&barr_ready);
+
+      if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  printf ("barrier wait failed: %m\n");
+	  exit (1);
+	}
+
+      int mode = prctl (PR_GET_FP_MODE);
+
+      if (mode != current_mode)
+	{
+	  printf ("unexpected mode: %d != %d\n", mode, current_mode);
+	  exit (1);
+	}
+
+      res = pthread_barrier_wait (&barr_cont);
+
+      if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  printf ("barrier wait failed: %m\n");
+	  exit (1);
+	}
+    }
+  return NULL;
+}
+
+int
+main (void)
+{
+  int count = sysconf (_SC_NPROCESSORS_ONLN);
+  if (count <= 0)
+    count = 1;
+  count *= 4;
+
+  pthread_t th[count];
+  int i;
+  int result = 0;
+
+  if (pthread_barrier_init (&barr_ready, NULL, count + 1) != 0)
+    {
+      printf ("failed to initialize barrier: %m\n");
+      exit (1);
+    }
+
+  if (pthread_barrier_init (&barr_cont, NULL, count + 1) != 0)
+    {
+      printf ("failed to initialize barrier: %m\n");
+      exit (1);
+    }
+
+  for (i = 0; i < count; ++i)
+    if (pthread_create (&th[i], NULL, thread_function, 0) != 0)
+      {
+	printf ("creation of thread %d failed\n", i);
+	exit (1);
+      }
+
+  for (i = 0 ; i < 7 ; i++)
+    {
+      if (prctl (PR_SET_FP_MODE, mode[i % 6]) != 0)
+	{
+	  if (errno != ENOTSUP)
+	    {
+	      printf ("prctl PR_SET_FP_MODE failed: %m");
+	      exit (1);
+	    }
+	}
+      else
+	current_mode = mode[i % 6];
+
+
+      int res = pthread_barrier_wait (&barr_ready);
+
+      if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  printf ("barrier wait failed: %m\n");
+	  exit (1);
+	}
+
+      if (i == 6)
+	finished = 1;
+
+      res = pthread_barrier_wait (&barr_cont);
+
+      if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  printf ("barrier wait failed: %m\n");
+	  exit (1);
+	}
+    }
+
+  for (i = 0; i < count; ++i)
+    {
+      void *v;
+      if (pthread_join (th[i], &v) != 0)
+	{
+	  printf ("join of thread %d failed\n", i);
+	  result = 1;
+	}
+      else if (v != NULL)
+	{
+	  printf ("join %d successful, but child failed\n", i);
+	  result = 1;
+	}
+      else
+	printf ("join %d successful\n", i);
+    }
+
+  return result;
+}
diff --git a/sysdeps/mips/tst-mode-switch-3.c b/sysdeps/mips/tst-mode-switch-3.c
new file mode 100644
index 0000000..9cc725b
--- /dev/null
+++ b/sysdeps/mips/tst-mode-switch-3.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <sys/prctl.h>
+
+#if __mips_fpr != 0 || _MIPS_SPFPSET != 16
+# error This test requires -mfpxx -mno-odd-spreg
+#endif
+
+/* This test verifies that mode changes between a setjmp and longjmp do
+   not corrupt the state of callee-saved registers.  */
+
+static int mode[6] =
+  {
+    0,
+    PR_FP_MODE_FR,
+    PR_FP_MODE_FR | PR_FP_MODE_FRE,
+    PR_FP_MODE_FR,
+    0,
+    PR_FP_MODE_FR | PR_FP_MODE_FRE
+  };
+static jmp_buf env;
+float check1 = 2.0;
+double check2 = 3.0;
+
+int
+main (void)
+{
+  int i;
+  int result = 0;
+
+  for (i = 0 ; i < 7 ; i++)
+    {
+      int retval;
+      register float test1 __asm ("$f20");
+      register double test2 __asm ("$f22");
+
+      /* Hide what we are doing to $f20 and $f22 from the compiler.  */
+      __asm __volatile ("l.s %0,%2\n"
+			"l.d %1,%3\n"
+			: "=f" (test1), "=f" (test2)
+			: "m" (check1), "m" (check2));
+
+      retval = setjmp (env);
+
+      /* Make sure the compiler knows we want to access the variables
+         via the named registers again.  */
+      __asm __volatile ("" : : "f" (test1), "f" (test2));
+
+      if (test1 != check1 || test2 != check2)
+	{
+	  printf ("Corrupt register detected: $20 %f = %f, $22 %f = %f\n",
+		  test1, check1, test2, check2);
+	  result = 1;
+	}
+
+      if (retval == 0)
+	{
+	  if (prctl (PR_SET_FP_MODE, mode[i % 6]) != 0
+	      && errno != ENOTSUP)
+	    {
+	      printf ("prctl PR_SET_FP_MODE failed: %m");
+	      exit (1);
+	    }
+	  longjmp (env, 0);
+	}
+    }
+
+  return result;
+}
diff --git a/sysdeps/unix/sysv/linux/mips/configure b/sysdeps/unix/sysv/linux/mips/configure
index 6db1f80..83f8b13 100644
--- a/sysdeps/unix/sysv/linux/mips/configure
+++ b/sysdeps/unix/sysv/linux/mips/configure
@@ -105,6 +105,148 @@ if test -z "$libc_mips_float"; then
   as_fn_error $? "could not determine if compiler is using hard or soft floating point ABI" "$LINENO" 5
 fi
 
+libc_mips_o32_fp=
+
+if test x"$libc_mips_abi" = xo32 -a x"$libc_mips_float" = xhard; then
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+      #if !defined(__mips_fpr)
+      #error Missing FPR sizes
+      #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+        #if (__mips_fpr != 32)
+        #error Not FP32
+        #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  libc_mips_o32_fp=32
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+          #if (__mips_fpr != 0) || !defined(_MIPS_SPFPSET) || (_MIPS_SPFPSET != 16)
+          #error Not FPXX (without odd single-precision registers)
+          #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  libc_mips_o32_fp=xx
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+            #if (__mips_fpr != 0)
+            #error Not FPXX (with odd single precision registers)
+            #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  libc_mips_o32_fp=xxo
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+              #if (__mips_fpr != 64) || !defined(_MIPS_SPFPSET) || (_MIPS_SPFPSET != 16)
+              #error Not FP64A
+              #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  libc_mips_o32_fp=64a
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                #if (__mips_fpr != 64)
+                #error Not FP64
+                #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  libc_mips_o32_fp=64
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+config_vars="$config_vars
+o32-fpabi = ${libc_mips_o32_fp}"
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+    #include <linux/prctl.h>
+    #if !defined(PR_GET_FP_MODE) || !defined(PR_SET_FP_MODE)
+    #error New prctl support for setting FP modes not found
+    #endif
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  libc_mips_mode_switch=yes
+else
+  libc_mips_mode_switch=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+config_vars="$config_vars
+mips-mode-switch = ${libc_mips_mode_switch}"
+
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
 $as_echo_n "checking for grep that handles long lines and -e... " >&6; }
diff --git a/sysdeps/unix/sysv/linux/mips/configure.ac b/sysdeps/unix/sysv/linux/mips/configure.ac
index c3f217a..5039ec9 100644
--- a/sysdeps/unix/sysv/linux/mips/configure.ac
+++ b/sysdeps/unix/sysv/linux/mips/configure.ac
@@ -44,6 +44,59 @@ if test -z "$libc_mips_float"; then
   AC_MSG_ERROR([could not determine if compiler is using hard or soft floating point ABI])
 fi
 
+libc_mips_o32_fp=
+
+if test x"$libc_mips_abi" = xo32 -a x"$libc_mips_float" = xhard; then
+  AC_COMPILE_IFELSE(
+    [AC_LANG_PROGRAM([
+      #if !defined(__mips_fpr)
+      #error Missing FPR sizes
+      #endif])],
+    [AC_COMPILE_IFELSE(
+      [AC_LANG_PROGRAM([
+        #if (__mips_fpr != 32)
+        #error Not FP32
+        #endif])],
+      [libc_mips_o32_fp=32],
+      [AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([
+          #if (__mips_fpr != 0) || !defined(_MIPS_SPFPSET) || (_MIPS_SPFPSET != 16)
+          #error Not FPXX (without odd single-precision registers)
+          #endif])],
+        [libc_mips_o32_fp=xx],
+        [AC_COMPILE_IFELSE(
+          [AC_LANG_PROGRAM([
+            #if (__mips_fpr != 0)
+            #error Not FPXX (with odd single precision registers)
+            #endif])],
+          [libc_mips_o32_fp=xxo],
+          [AC_COMPILE_IFELSE(
+            [AC_LANG_PROGRAM([
+              #if (__mips_fpr != 64) || !defined(_MIPS_SPFPSET) || (_MIPS_SPFPSET != 16)
+              #error Not FP64A
+              #endif])],
+            [libc_mips_o32_fp=64a],
+            [AC_COMPILE_IFELSE(
+              [AC_LANG_PROGRAM([
+                #if (__mips_fpr != 64)
+                #error Not FP64
+                #endif])],
+              [libc_mips_o32_fp=64],
+              [])])])])])],
+    [])
+fi
+LIBC_CONFIG_VAR([o32-fpabi],[${libc_mips_o32_fp}])
+
+AC_COMPILE_IFELSE(
+  [AC_LANG_PROGRAM([
+    #include <linux/prctl.h>
+    #if !defined(PR_GET_FP_MODE) || !defined(PR_SET_FP_MODE)
+    #error New prctl support for setting FP modes not found
+    #endif])],
+  [libc_mips_mode_switch=yes],
+  [libc_mips_mode_switch=no])
+LIBC_CONFIG_VAR([mips-mode-switch],[${libc_mips_mode_switch}])
+
 AC_CACHE_CHECK([whether the compiler is using the 2008 NaN encoding],
   libc_cv_mips_nan2008, [AC_EGREP_CPP(yes, [dnl
 #ifdef __mips_nan2008
diff --git a/sysdeps/unix/sysv/linux/mips/ldsodefs.h b/sysdeps/unix/sysv/linux/mips/ldsodefs.h
index d7c62f4..70f8f16 100644
--- a/sysdeps/unix/sysv/linux/mips/ldsodefs.h
+++ b/sysdeps/unix/sysv/linux/mips/ldsodefs.h
@@ -34,7 +34,7 @@ extern void _dl_static_init (struct link_map *map);
 #undef VALID_ELF_ABIVERSION
 #define VALID_ELF_ABIVERSION(osabi,ver)			\
   (ver == 0						\
-   || (osabi == ELFOSABI_SYSV && ver < 2)		\
+   || (osabi == ELFOSABI_SYSV && ver < 4)		\
    || (osabi == ELFOSABI_GNU && ver < LIBC_ABI_MAX))
 
 #endif /* ldsodefs.h */
diff --git a/sysdeps/unix/sysv/linux/mips/libc-abis b/sysdeps/unix/sysv/linux/mips/libc-abis
index f180a03..14ff603 100644
--- a/sysdeps/unix/sysv/linux/mips/libc-abis
+++ b/sysdeps/unix/sysv/linux/mips/libc-abis
@@ -11,3 +11,6 @@ MIPS_PLT	mips*-*-linux*
 # Unique symbol definitions for C++.
 # Architecture independent, all ELF targets (== all targets)
 UNIQUE
+#
+# MIPS O32 FP64
+MIPS_O32_FP64   mips*-*-linux*

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                 |   50 ++
 elf/elf.h                                 |  103 ++++-
 sysdeps/mips/Makefile                     |   50 ++
 sysdeps/mips/bits/linkmap.h               |    2 +
 sysdeps/mips/dl-machine-reject-phdr.h     |  326 +++++++++++
 sysdeps/mips/dl-machine.h                 |    5 +
 sysdeps/mips/tst-abi-fp32mod.c            |   22 +
 sysdeps/mips/tst-abi-fp64amod.c           |   22 +
 sysdeps/mips/tst-abi-fp64mod.c            |   22 +
 sysdeps/mips/tst-abi-fpxxmod.c            |   22 +
 sysdeps/mips/tst-abi-fpxxomod.c           |   22 +
 sysdeps/mips/tst-abi-interlink.c          |  844 +++++++++++++++++++++++++++++
 sysdeps/mips/tst-mode-switch-1.c          |  123 +++++
 sysdeps/mips/tst-mode-switch-2.c          |  163 ++++++
 sysdeps/mips/tst-mode-switch-3.c          |   90 +++
 sysdeps/unix/sysv/linux/mips/configure    |  142 +++++
 sysdeps/unix/sysv/linux/mips/configure.ac |   53 ++
 sysdeps/unix/sysv/linux/mips/ldsodefs.h   |    2 +-
 sysdeps/unix/sysv/linux/mips/libc-abis    |    3 +
 19 files changed, 2062 insertions(+), 4 deletions(-)
 create mode 100644 sysdeps/mips/dl-machine-reject-phdr.h
 create mode 100644 sysdeps/mips/tst-abi-fp32mod.c
 create mode 100644 sysdeps/mips/tst-abi-fp64amod.c
 create mode 100644 sysdeps/mips/tst-abi-fp64mod.c
 create mode 100644 sysdeps/mips/tst-abi-fpxxmod.c
 create mode 100644 sysdeps/mips/tst-abi-fpxxomod.c
 create mode 100644 sysdeps/mips/tst-abi-interlink.c
 create mode 100644 sysdeps/mips/tst-mode-switch-1.c
 create mode 100644 sysdeps/mips/tst-mode-switch-2.c
 create mode 100644 sysdeps/mips/tst-mode-switch-3.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]