diff mbox

[RFC,5/5] MIPS: Add IEEE Std 754 NaN interlinking support

Message ID alpine.DEB.2.00.1511200640560.6915@tp.orcam.me.uk
State New
Headers show

Commit Message

Maciej W. Rozycki Nov. 20, 2015, 7:31 a.m. UTC
Add the dynamic loading part of IEEE Std 754 NaN interlinking support, 
as per "MIPS ABI Extension for IEEE Std 754 Non-Compliant Interlinking" 
<https://dmz-portal.mips.com/wiki/MIPS_ABI_-_NaN_Interlinking>:

* interpret the MIPS_AFL_FLAGS1_IEEE and MIPS_AFL_FLAGS2_RELAXED flags
  in the PT_MIPS_ABIFLAGS segment of binaries processed,

* interpret the AT_FLAGS entry in the auxiliary vector supplied by the 
  kernel,

* accept or reject ELF executables (where explicitly loaded) and dynamic 
  shared objects accordingly, in the run-time loader,

* handle relaxed IEEE Std 754 compliance mode binaries in `ld.so.cache',

* handle kernel IEEE Std 754 compliance mode switching via prctl(2) in 
  explicit `ld.so' invocation.

Also add messages for ENOEXEC and ELIBBAD error codes produced with code 
added here to `ld.so's minimal `__strerror_r' implementation.  Other 
codes such as ENOSYS or EOPNOTSUPP may be produced by the kernel in 
response to the prctl(2) syscall.  Let's not pull in the whole list 
though.

	* elf/elf.h (MIPS_AFL_FLAGS1_IEEE): New macro.
	(MIPS_AFL_FLAGS2_NOWARN, MIPS_AFL_FLAGS2_RELAXED): Likewise.
	(AV_FLAGS_MIPS_RELAXED): Likewise.
	* include/link.h (link_map) [NEED_L_EFLAGS]: Add `l_eflags' 
	struct member.
	* sysdeps/generic/ldconfig.h (FLAG_MIPS_LIB32_RELAXED): New 
	macro.
	(FLAG_MIPS64_LIBN32_RELAXED): Likewise.
	(FLAG_MIPS64_LIBN64_RELAXED): Likewise.
	* sysdeps/mips/dl-machine-main-map-setup.h: New file.
	* sysdeps/mips/dl-machine-reject-phdr.h 
	(elf_machine_reject_phdr_p): Permit MIPS_AFL_FLAGS2_RELAXED set 
	in `mips_abiflags'.  Reject relaxed IEEE Std 754 compliance mode 
	libraries in the strict IEEE Std 754 compliance mode.
	* sysdeps/mips/dl-machine.h (ELF_MACHINE_NAN2008): Move macro
	definition along with the associated consistency check over to
	sysdeps/mips/ldsodefs.h.
	(elf_machine_matches_host): Do not reject libraries using the 
	opposite NaN encoding in the relaxed IEEE Std 754 compliance 
	mode.
	* sysdeps/mips/ldsodefs.h (ELF_MACHINE_NAN2008): Add macro
	definition along with the associated consistency check moved 
	from sysdeps/mips/dl-machine.h.
	* sysdeps/mips/linkmap.h (link_map_machine): Convert `odd_spreg' 
	member to a bitfield.  Add `ieee' and `relaxed' members.
	(NEED_L_EFLAGS): New macro.
	* sysdeps/unix/sysv/linux/mips/dl-cache.h (_DL_CACHE_OTHER_ID):
	New macro.
	(_DL_CACHE_RELAXED_ID): Likewise.
	[_DL_CACHE_DEFAULT_ID]: Remove condition.
	(_dl_cache_check_flags): Rewrite for relaxed IEEE Std 754 
	compliance mode handling.
	* elf/cache.c (print_entry): Handle the FLAG_MIPS_LIB32_RELAXED,
	FLAG_MIPS64_LIBN32_RELAXED and FLAG_MIPS64_LIBN64_RELAXED flags 
	in entries.
	* elf/dl-load.c (_dl_map_object_from_fd) [NEED_L_EFLAGS]: Record
	the ELF file header flags in the link map newly created.
	* elf/dl-minimal.c (__strerror_r) <ENOEXEC, ELIBBAD>: New cases
	with messages for error codes.
	* sysdeps/mips/dl-abiflags-cache.c (__mips_abiflags_cache): 
	Permit MIPS_AFL_FLAGS2_RELAXED set in `mips_abiflags'.  Record 
	any MIPS_AFL_FLAGS1_IEEE and MIPS_AFL_FLAGS2_RELAXED flags set.
	* sysdeps/unix/sysv/linux/mips/readelflib.c (check_ptr): New 
	macro.
	(get_relaxed): Likewise.
	(process_elf_file): Handle relaxed IEEE Std 754 compliance mode
	binaries.
---
glibc-mips-nan-interlink.diff
diff mbox

Patch

Index: glibc/elf/cache.c
===================================================================
--- glibc.orig/elf/cache.c	2015-11-16 18:45:47.523597318 +0000
+++ glibc/elf/cache.c	2015-11-16 19:19:32.978099770 +0000
@@ -114,6 +114,15 @@  print_entry (const char *lib, int flag, 
     case FLAG_MIPS64_LIBN64_NAN2008:
       fputs (",64bit,nan2008", stdout);
       break;
+    case FLAG_MIPS_LIB32_RELAXED:
+      fputs (",relaxed", stdout);
+      break;
+    case FLAG_MIPS64_LIBN32_RELAXED:
+      fputs (",N32,relaxed", stdout);
+      break;
+    case FLAG_MIPS64_LIBN64_RELAXED:
+      fputs (",64bit,relaxed", stdout);
+      break;
     case 0:
       break;
     default:
Index: glibc/elf/dl-load.c
===================================================================
--- glibc.orig/elf/dl-load.c	2015-11-16 19:19:18.815602519 +0000
+++ glibc/elf/dl-load.c	2015-11-16 19:19:32.981146011 +0000
@@ -1018,6 +1018,9 @@  _dl_map_object_from_fd (const char *name
   l->l_entry = header->e_entry;
   type = header->e_type;
   l->l_phnum = header->e_phnum;
+#ifdef NEED_L_EFLAGS
+  l->l_eflags = header->e_flags;
+#endif
 
   maplength = header->e_phnum * sizeof (ElfW(Phdr));
   if (header->e_phoff + maplength <= (size_t) fbp->len)
Index: glibc/elf/dl-minimal.c
===================================================================
--- glibc.orig/elf/dl-minimal.c	2015-11-16 18:45:47.558974685 +0000
+++ glibc/elf/dl-minimal.c	2015-11-16 19:19:32.985198148 +0000
@@ -178,9 +178,15 @@  __strerror_r (int errnum, char *buf, siz
     case EIO:
       msg = (char *) "Input/output error";
       break;
+    case ENOEXEC:
+      msg = (char *) "Exec format error";
+      break;
     case EACCES:
       msg = (char *) "Permission denied";
       break;
+    case ELIBBAD:
+      msg = (char *) "Accessing a corrupted shared library";
+      break;
     default:
       /* No need to check buffer size, all calls in the dynamic linker
 	 provide enough space.  */
Index: glibc/elf/elf.h
===================================================================
--- glibc.orig/elf/elf.h	2015-11-16 18:45:47.612452757 +0000
+++ glibc/elf/elf.h	2015-11-16 19:19:32.996292254 +0000
@@ -1858,7 +1858,18 @@  typedef struct
 #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.  */
+#define MIPS_AFL_FLAGS1_IEEE	  2  /* IEEE Std 754 compliance mode selection
+					requested.  */
+
+/* Masks for the flags2 word of an ABI flags structure.  */
+
+#define MIPS_AFL_FLAGS2_NOWARN	  1  /* Do not warn on linking a strict object
+					in the IEEE Std 754 relaxed compliance
+					mode.  */
+#define MIPS_AFL_FLAGS2_RELAXED	  2  /* IEEE Std 754 relaxed compliance mode
+					selected.  */
 
 /* Object attribute values.  */
 enum
@@ -1883,6 +1894,10 @@  enum
   Val_GNU_MIPS_ABI_FP_MAX = 7
 };
 
+/* Masks for the value passed in an AT_FLAGS auxiliary vector entry.  */
+
+#define AV_FLAGS_MIPS_RELAXED	(1U << 25) /* Relaxed IEEE Std 754 mode.  */
+
 /* HPPA specific definitions.  */
 
 /* Legal values for e_flags field of Elf32_Ehdr.  */
Index: glibc/include/link.h
===================================================================
--- glibc.orig/include/link.h	2015-11-16 18:45:47.663487267 +0000
+++ glibc/include/link.h	2015-11-16 19:19:33.007514833 +0000
@@ -127,6 +127,9 @@  struct link_map
     ElfW(Addr) l_entry;		/* Entry point location.  */
     ElfW(Half) l_phnum;		/* Number of program header entries.  */
     ElfW(Half) l_ldnum;		/* Number of dynamic segment entries.  */
+#ifdef NEED_L_EFLAGS
+    ElfW (Word) l_eflags;	/* A copy of ELF header's e_flags.  */
+#endif
 
     /* Array of DT_NEEDED dependencies and their dependencies, in
        dependency order for symbol lookup (with and without
Index: glibc/sysdeps/generic/ldconfig.h
===================================================================
--- glibc.orig/sysdeps/generic/ldconfig.h	2015-11-16 18:45:47.705295568 +0000
+++ glibc/sysdeps/generic/ldconfig.h	2015-11-16 19:19:33.018684063 +0000
@@ -42,6 +42,9 @@ 
 #define FLAG_MIPS_LIB32_NAN2008		0x0c00
 #define FLAG_MIPS64_LIBN32_NAN2008	0x0d00
 #define FLAG_MIPS64_LIBN64_NAN2008	0x0e00
+#define FLAG_MIPS_LIB32_RELAXED		0x0f00
+#define FLAG_MIPS64_LIBN32_RELAXED	0x1000
+#define FLAG_MIPS64_LIBN64_RELAXED	0x1100
 
 /* Name of auxiliary cache.  */
 #define _PATH_LDCONFIG_AUX_CACHE "/var/cache/ldconfig/aux-cache"
Index: glibc/sysdeps/mips/dl-abiflags-cache.c
===================================================================
--- glibc.orig/sysdeps/mips/dl-abiflags-cache.c	2015-11-16 19:19:31.544056463 +0000
+++ glibc/sysdeps/mips/dl-abiflags-cache.c	2015-11-16 19:19:33.022772233 +0000
@@ -49,18 +49,29 @@  __mips_abiflags_cache (struct link_map *
 
 	  mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (l->l_addr + ph->p_vaddr);
 
-	  if (__glibc_unlikely (mips_abiflags->flags2 != 0))
-	    FAIL ("   %s: unknown MIPS.abiflags flags2: %u\n", l->l_name,
-		  mips_abiflags->flags2);
+	  Elf32_Word flags1 = mips_abiflags->flags1;
+	  Elf32_Word flags2 = mips_abiflags->flags2;
+
+	  if (__glibc_unlikely ((flags2 & ~MIPS_AFL_FLAGS2_RELAXED) != 0))
+	    FAIL ("   %s: unknown MIPS.abiflags flags2: %u\n",
+		  l->l_name, flags2);
+
+	  if (__glibc_unlikely ((flags1 & MIPS_AFL_FLAGS1_IEEE) == 0
+				&& (flags2 & MIPS_AFL_FLAGS2_RELAXED) != 0))
+	    FAIL ("   %s: unsupported MIPS.abiflags flags1/flags2: %u/%u\n",
+		  l->l_name, flags1, flags2);
 
 	  l->l_mach.fpabi = mips_abiflags->fp_abi;
-	  l->l_mach.odd_spreg = (mips_abiflags->flags1
-				 & MIPS_AFL_FLAGS1_ODDSPREG) != 0;
+	  l->l_mach.odd_spreg = (flags1 & MIPS_AFL_FLAGS1_ODDSPREG) != 0;
+	  l->l_mach.ieee = (flags1 & MIPS_AFL_FLAGS1_IEEE) != 0;
+	  l->l_mach.relaxed = (flags2 & MIPS_AFL_FLAGS2_RELAXED) != 0;
 	}
       else
 	{
 	  l->l_mach.fpabi = -1;
 	  l->l_mach.odd_spreg = true;
+	  l->l_mach.ieee = false;
+	  l->l_mach.relaxed = false;
 	}
     }
   return true;
Index: glibc/sysdeps/mips/dl-machine-main-map-setup.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ glibc/sysdeps/mips/dl-machine-main-map-setup.h	2015-11-16 19:19:33.025837918 +0000
@@ -0,0 +1,114 @@ 
+/* Machine-dependent main link map setup for the ELF loader, MIPS version.
+   Copyright (C) 2015 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_MAIN_MAP_SETUP
+#define _DL_MACHINE_MAIN_MAP_SETUP 1
+
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/prctl.h>
+
+#include <dl-abiflags-cache.h>
+
+#ifndef PR_SET_IEEE754_MODE
+# define PR_SET_IEEE754_MODE	48
+
+# define PR_IEEE754_MODE_LEGACY		0	/* Legacy mode.  */
+# define PR_IEEE754_MODE_STRICT		1	/* Strict mode.  */
+# define PR_IEEE754_MODE_RELAXED	2	/* Relaxed mode.  */
+
+# define PR_IEEE754_MODE_NAN_LEGACY	0	/* Set legacy NaN encoding.  */
+# define PR_IEEE754_MODE_NAN_2008	1	/* Set 2008 NaN encoding.  */
+#endif /* PR_SET_IEEE754_MODE */
+
+#define AV_FLAGS_SYSTEM_SHIFT	24		/* System AT_FLAGS shift.  */
+
+/* Set up machine-dependent parts of the main link map.  Return 0
+   if successful.  */
+
+static inline int
+elf_machine_main_map_setup (struct link_map *map, bool rtld_is_main)
+{
+  if (!__mips_abiflags_cache (map))
+    return ENOEXEC;
+
+#ifdef SHARED
+  /* Handle the 2008-NaN vs legacy-NaN compatibility check for the
+     main executable loaded through ld.so invoked directly.  */
+  if (rtld_is_main)
+    {
+      struct link_map *rtld_map = &GL (dl_rtld_map);
+
+      if (!__mips_abiflags_cache (rtld_map))
+	return ELIBBAD;
+
+      uintptr_t flags = GLRO (dl_flags) & ((1UL << AV_FLAGS_SYSTEM_SHIFT) - 1);
+      ElfW (Word) map_nan2008 = map->l_eflags & EF_MIPS_NAN2008;
+      bool map_nan_opposite = map_nan2008 != ELF_MACHINE_NAN2008;
+      int nan = (map_nan2008 ? PR_IEEE754_MODE_NAN_2008
+		 : PR_IEEE754_MODE_NAN_LEGACY);
+      int res;
+
+      if (FILE_IEEE_STRICT (map))
+	{
+	  if (FILE_IEEE_RELAXED (rtld_map) || map_nan_opposite)
+	    return ELIBBAD;
+	  if (FLAGS_IEEE_RELAXED (GLRO (dl_flags)))
+	    {
+	      res = prctl (PR_SET_IEEE754_MODE, PR_IEEE754_MODE_STRICT, nan);
+	      if (res < 0)
+		return errno;
+	      flags |= res << AV_FLAGS_SYSTEM_SHIFT;
+	      GLRO (dl_flags) = flags;
+	    }
+	}
+
+      if (FILE_IEEE_RELAXED (map)
+	  && (FLAGS_IEEE_STRICT (GLRO (dl_flags)) || map_nan_opposite))
+	{
+	  res = prctl (PR_SET_IEEE754_MODE, PR_IEEE754_MODE_RELAXED, nan);
+	  if (res < 0)
+	    return errno;
+	  flags |= res << AV_FLAGS_SYSTEM_SHIFT;
+	  GLRO (dl_flags) = flags;
+	}
+
+      if (FILE_IEEE_LEGACY (map))
+	{
+	  if (FILE_IEEE_LEGACY (rtld_map)
+	      && FLAGS_IEEE_STRICT (GLRO (dl_flags)) && map_nan_opposite)
+	    return ELIBBAD;
+	  if (!FILE_IEEE_LEGACY (rtld_map) || map_nan_opposite)
+	    {
+	      res = prctl (PR_SET_IEEE754_MODE, PR_IEEE754_MODE_LEGACY, nan);
+	      if (res < 0)
+		return errno;
+	      flags |= res << AV_FLAGS_SYSTEM_SHIFT;
+	      if (FLAGS_IEEE_STRICT (flags)
+		  && (FILE_IEEE_RELAXED (rtld_map) || map_nan_opposite))
+		return ELIBBAD;
+	      GLRO (dl_flags) = flags;
+	    }
+	}
+    }
+#endif
+
+  return 0;
+}
+
+#endif /* dl-machine-main-map-setup.h */
Index: glibc/sysdeps/mips/dl-machine-reject-phdr.h
===================================================================
--- glibc.orig/sysdeps/mips/dl-machine-reject-phdr.h	2015-11-16 19:19:31.578840360 +0000
+++ glibc/sysdeps/mips/dl-machine-reject-phdr.h	2015-11-16 19:19:33.027867492 +0000
@@ -115,6 +115,8 @@  elf_machine_reject_phdr_p (const ElfW(Ph
   struct link_map *l;
   Lmid_t nsid;
   int in_abi = -1;
+  bool ieee = false;
+  bool relaxed = false;
   struct abi_req in_req;
   Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL;
   bool perfect_match = false;
@@ -146,12 +148,36 @@  elf_machine_reject_phdr_p (const ElfW(Ph
       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);
+      Elf32_Word flags1 = mips_abiflags->flags1;
+      Elf32_Word flags2 = mips_abiflags->flags2;
+
+      if (__glibc_unlikely ((flags2 & ~MIPS_AFL_FLAGS2_RELAXED) != 0))
+	REJECT ("   unknown MIPS.abiflags flags2: %u\n", flags2);
+
+      if (__glibc_unlikely ((flags1 & MIPS_AFL_FLAGS1_IEEE) == 0
+			    && (flags2 & MIPS_AFL_FLAGS2_RELAXED) != 0))
+	REJECT ("   unsupported MIPS.abiflags flags1/flags2: %u/%u\n",
+		flags1, flags2);
 
       in_abi = mips_abiflags->fp_abi;
+      ieee = (mips_abiflags->flags1 & MIPS_AFL_FLAGS1_IEEE) != 0;
+      relaxed = (mips_abiflags->flags2 & MIPS_AFL_FLAGS2_RELAXED) != 0;
     }
 
+  /* A 2008-NaN vs legacy-NaN mode check for strict IEEE Std 754 compliance
+     executables will have already been made in `elf_machine_matches_host'.
+     All that remains to be done for such executables is to guarantee we
+     don't pull a relaxed IEEE Std 754 compliance DSO into one.
+
+     Again, watch out for the case where there's no main map.  This happens
+     when an executable is being loaded through ld.so invoked directly.  The
+     check is deferred to `elf_machine_main_map_setup' then.  */
+  struct link_map *main_map = GL (dl_ns)[LM_ID_BASE]._ns_loaded;
+  if (main_map != NULL
+      && FLAGS_IEEE_STRICT (GLRO (dl_flags))
+      && ieee && relaxed)
+    return true;
+
   /* ANY is compatible with anything.  */
   perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY);
 
Index: glibc/sysdeps/mips/dl-machine.h
===================================================================
--- glibc.orig/sysdeps/mips/dl-machine.h	2015-11-16 18:45:47.767072146 +0000
+++ glibc/sysdeps/mips/dl-machine.h	2015-11-16 19:19:33.039004979 +0000
@@ -83,16 +83,6 @@  do { if ((l)->l_info[DT_MIPS (RLD_MAP_RE
        (ElfW(Addr)) (r); \
    } while (0)
 
-#if ((defined __mips_nan2008 && !defined HAVE_MIPS_NAN2008) \
-     || (!defined __mips_nan2008 && defined HAVE_MIPS_NAN2008))
-# error "Configuration inconsistency: __mips_nan2008 != HAVE_MIPS_NAN2008, overridden CFLAGS?"
-#endif
-#ifdef __mips_nan2008
-# define ELF_MACHINE_NAN2008 EF_MIPS_NAN2008
-#else
-# define ELF_MACHINE_NAN2008 0
-#endif
-
 /* Return nonzero iff ELF header is compatible with the running host.  */
 static inline int __attribute_used__
 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
@@ -103,8 +93,15 @@  elf_machine_matches_host (const ElfW(Ehd
     return 0;
 #endif
 
-  /* Don't link 2008-NaN and legacy-NaN objects together.  */
-  if ((ehdr->e_flags & EF_MIPS_NAN2008) != ELF_MACHINE_NAN2008)
+  /* If we're in a strict IEEE Std 754 compliance executable, then don't
+     link 2008-NaN and legacy-NaN objects together.  Watch out for the
+     case where there's no main map.  This happens when an executable is
+     being loaded through ld.so invoked directly.  The check is deferred
+     to `elf_machine_main_map_setup' then.  */
+  struct link_map *main_map = GL (dl_ns)[LM_ID_BASE]._ns_loaded;
+  if (main_map != NULL
+      && FLAGS_IEEE_STRICT (GLRO (dl_flags))
+      && (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
Index: glibc/sysdeps/mips/ldsodefs.h
===================================================================
--- glibc.orig/sysdeps/mips/ldsodefs.h	2015-11-16 18:45:47.779230991 +0000
+++ glibc/sysdeps/mips/ldsodefs.h	2015-11-16 19:19:33.043078738 +0000
@@ -146,4 +146,20 @@  typedef struct
 #undef ELF64_R_INFO
 #define ELF64_R_INFO(sym, type) ELF64_MIPS_R_INFO ((sym), (type))
 
+#if ((defined __mips_nan2008 && !defined HAVE_MIPS_NAN2008) \
+     || (!defined __mips_nan2008 && defined HAVE_MIPS_NAN2008))
+# error "Configuration inconsistency: __mips_nan2008 != HAVE_MIPS_NAN2008, overridden CFLAGS?"
+#endif
+#ifdef __mips_nan2008
+# define ELF_MACHINE_NAN2008 EF_MIPS_NAN2008
+#else
+# define ELF_MACHINE_NAN2008 0
+#endif
+
+#define FLAGS_IEEE_RELAXED(f)	(!FLAGS_IEEE_STRICT (f))
+#define FLAGS_IEEE_STRICT(f)	(!((f) & AV_FLAGS_MIPS_RELAXED))
+#define FILE_IEEE_RELAXED(l)	((l)->l_mach.ieee && (l)->l_mach.relaxed)
+#define FILE_IEEE_STRICT(l)	((l)->l_mach.ieee && !(l)->l_mach.relaxed)
+#define FILE_IEEE_LEGACY(l)	(!(l)->l_mach.ieee)
+
 #endif
Index: glibc/sysdeps/mips/linkmap.h
===================================================================
--- glibc.orig/sysdeps/mips/linkmap.h	2015-11-16 19:19:28.267518460 +0000
+++ glibc/sysdeps/mips/linkmap.h	2015-11-16 19:19:33.046102538 +0000
@@ -20,5 +20,11 @@  struct link_map_machine
   {
     ElfW (Addr) plt;		/* Address of .plt.  */
     ElfW (Word) fpabi;		/* FP ABI of the object.  */
-    unsigned int odd_spreg;	/* Odd single-precision registers used? */
+    unsigned int odd_spreg:1;	/* Odd single-precision registers used? */
+    unsigned int ieee:1;	/* IEEE Std 754 compliance mode selection
+				   requested?  */
+    unsigned int relaxed:1;	/* IEEE Std 754 relaxed compliance mode
+				   selected?  */
   };
+
+#define NEED_L_EFLAGS
Index: glibc/sysdeps/unix/sysv/linux/mips/dl-cache.h
===================================================================
--- glibc.orig/sysdeps/unix/sysv/linux/mips/dl-cache.h	2015-11-16 18:45:47.824613202 +0000
+++ glibc/sysdeps/unix/sysv/linux/mips/dl-cache.h	2015-11-16 19:19:33.059455958 +0000
@@ -23,29 +23,64 @@ 
 # error "Configuration inconsistency: __mips_nan2008 != HAVE_MIPS_NAN2008, overridden CFLAGS?"
 #endif
 
-/* Redefine the cache ID for new ABIs and 2008 NaN support; legacy o32
-   keeps using the generic check.  */
 #ifdef __mips_nan2008
 # if _MIPS_SIM == _ABIO32
 #  define _DL_CACHE_DEFAULT_ID	(FLAG_MIPS_LIB32_NAN2008 | FLAG_ELF_LIBC6)
+#  define _DL_CACHE_OTHER_ID	FLAG_ELF_LIBC6
 # elif _MIPS_SIM == _ABI64
 #  define _DL_CACHE_DEFAULT_ID	(FLAG_MIPS64_LIBN64_NAN2008 | FLAG_ELF_LIBC6)
+#  define _DL_CACHE_OTHER_ID	(FLAG_MIPS64_LIBN64 | FLAG_ELF_LIBC6)
 # elif _MIPS_SIM == _ABIN32
 #  define _DL_CACHE_DEFAULT_ID	(FLAG_MIPS64_LIBN32_NAN2008 | FLAG_ELF_LIBC6)
+#  define _DL_CACHE_OTHER_ID	(FLAG_MIPS64_LIBN32 | FLAG_ELF_LIBC6)
 # endif
 #else
-# if _MIPS_SIM == _ABI64
+# if _MIPS_SIM == _ABIO32
+#  define _DL_CACHE_DEFAULT_ID	FLAG_ELF_LIBC6
+#  define _DL_CACHE_OTHER_ID	(FLAG_MIPS_LIB32_NAN2008 | FLAG_ELF_LIBC6)
+# elif _MIPS_SIM == _ABI64
 #  define _DL_CACHE_DEFAULT_ID	(FLAG_MIPS64_LIBN64 | FLAG_ELF_LIBC6)
+#  define _DL_CACHE_OTHER_ID	(FLAG_MIPS64_LIBN64_NAN2008 | FLAG_ELF_LIBC6)
 # elif _MIPS_SIM == _ABIN32
 #  define _DL_CACHE_DEFAULT_ID	(FLAG_MIPS64_LIBN32 | FLAG_ELF_LIBC6)
+#  define _DL_CACHE_OTHER_ID	(FLAG_MIPS64_LIBN32_NAN2008 | FLAG_ELF_LIBC6)
 # endif
 #endif
-
-#ifdef _DL_CACHE_DEFAULT_ID
-# define _dl_cache_check_flags(flags) \
-  ((flags) == _DL_CACHE_DEFAULT_ID)
+#if _MIPS_SIM == _ABIO32
+# define _DL_CACHE_RELAXED_ID	(FLAG_MIPS_LIB32_RELAXED | FLAG_ELF_LIBC6)
+#elif _MIPS_SIM == _ABI64
+# define _DL_CACHE_RELAXED_ID	(FLAG_MIPS64_LIBN64_RELAXED | FLAG_ELF_LIBC6)
+#elif _MIPS_SIM == _ABIN32
+# define _DL_CACHE_RELAXED_ID	(FLAG_MIPS64_LIBN32_RELAXED | FLAG_ELF_LIBC6)
 #endif
 
+#define _dl_cache_check_flags(flags)					\
+  ({									\
+    bool ok;								\
+									\
+    switch (flags)							\
+      {									\
+      case FLAG_ELF:							\
+	if (_DL_CACHE_OTHER_ID == FLAG_ELF_LIBC6)			\
+	  ok = GL (dl_ns)[LM_ID_BASE]._ns_loaded->l_mach.relaxed;	\
+	else								\
+	  ok = _DL_CACHE_DEFAULT_ID == FLAG_ELF_LIBC6;			\
+	break;								\
+      case _DL_CACHE_DEFAULT_ID:					\
+	ok = true;							\
+	break;								\
+      case _DL_CACHE_OTHER_ID:						\
+      case _DL_CACHE_RELAXED_ID:					\
+	ok = GL (dl_ns)[LM_ID_BASE]._ns_loaded->l_mach.relaxed;		\
+	break;								\
+      default:								\
+	ok = false;							\
+	break;								\
+      }									\
+									\
+    ok;									\
+  })
+
 #define add_system_dir(dir) \
   do								\
     {								\
Index: glibc/sysdeps/unix/sysv/linux/mips/readelflib.c
===================================================================
--- glibc.orig/sysdeps/unix/sysv/linux/mips/readelflib.c	2015-11-16 18:45:47.861849442 +0000
+++ glibc/sysdeps/unix/sysv/linux/mips/readelflib.c	2015-11-16 19:19:33.064588851 +0000
@@ -19,6 +19,64 @@ 
    License along with the GNU C Library.  If not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* check_ptr checks that a pointer is in the mmaped file and doesn't
+   point outside it.  */
+#undef check_ptr
+#define check_ptr(ptr, size)						      \
+  do									      \
+    {									      \
+      void *_ptr = (ptr);						      \
+									      \
+      if (_ptr < file_contents						      \
+	  || _ptr + size > file_contents + file_length)			      \
+	{								      \
+	  error (0, 0, _("file %s is truncated\n"), file_name);		      \
+	  return 1;							      \
+	}								      \
+    }									      \
+  while (0);
+
+#define get_relaxed(elfsz)						      \
+  ({									      \
+    bool relaxed = false;						      \
+    Elf##elfsz##_Phdr *seg;						      \
+    int i;								      \
+									      \
+    for (i = 0, seg = elf_header.eh##elfsz->e_phoff + file_contents;	      \
+	 i < elf_header.eh##elfsz->e_phnum;				      \
+	 i++, seg++)							      \
+      {									      \
+	check_ptr (seg, sizeof (*seg));					      \
+	if (seg->p_type == PT_MIPS_ABIFLAGS)				      \
+	  {								      \
+	    Elf_MIPS_ABIFlags_v0 *abiflags;				      \
+									      \
+	    if (seg->p_filesz < sizeof (*abiflags))			      \
+	      {								      \
+		error (0, 0,						      \
+		       _("Malformed PT_MIPS_ABIFLAGS in file %s.\n"),	      \
+		       file_name);					      \
+		return 1;						      \
+	      }								      \
+									      \
+	    abiflags = seg->p_offset + file_contents;			      \
+	    check_ptr (abiflags, sizeof (*abiflags));			      \
+									      \
+	    if ((abiflags->flags2 & ~MIPS_AFL_FLAGS2_RELAXED) != 0)	      \
+	      {								      \
+		error (0, 0,						      \
+		       _("Unknown MIPS.abiflags flags2 %x in file %s.\n"),    \
+		       abiflags->flags2, file_name);			      \
+		return 1;						      \
+	      }								      \
+									      \
+	    relaxed = (abiflags->flags2 & MIPS_AFL_FLAGS2_RELAXED) != 0;      \
+	    break;							      \
+	  }								      \
+      }									      \
+									      \
+    relaxed;								      \
+  })
 
 int process_elf32_file (const char *file_name, const char *lib, int *flag,
 			unsigned int *osversion, char **soname,
@@ -51,11 +109,15 @@  process_elf_file (const char *file_name,
 	{
 	  Elf32_Word flags = elf_header.eh32->e_flags;
 	  int nan2008 = (flags & EF_MIPS_NAN2008) != 0;
+	  bool relaxed = get_relaxed (32);
 
 	  /* n32 libraries are always libc.so.6+, o32 only if 2008 NaN.  */
 	  if ((flags & EF_MIPS_ABI2) != 0)
-	    *flag = (nan2008 ? FLAG_MIPS64_LIBN32_NAN2008
+	    *flag = (relaxed ? FLAG_MIPS64_LIBN32_RELAXED
+		     : nan2008 ? FLAG_MIPS64_LIBN32_NAN2008
 		     : FLAG_MIPS64_LIBN32) | FLAG_ELF_LIBC6;
+	  else if (relaxed)
+	    *flag = FLAG_MIPS_LIB32_RELAXED | FLAG_ELF_LIBC6;
 	  else if (nan2008)
 	    *flag = FLAG_MIPS_LIB32_NAN2008 | FLAG_ELF_LIBC6;
 	}
@@ -69,8 +131,10 @@  process_elf_file (const char *file_name,
 	{
 	  Elf64_Word flags = elf_header.eh64->e_flags;
 	  int nan2008 = (flags & EF_MIPS_NAN2008) != 0;
+	  bool relaxed = get_relaxed (64);
 
-	  *flag = (nan2008 ? FLAG_MIPS64_LIBN64_NAN2008
+	  *flag = (relaxed ? FLAG_MIPS64_LIBN64_RELAXED
+		   : nan2008 ? FLAG_MIPS64_LIBN64_NAN2008
 		   : FLAG_MIPS64_LIBN64) | FLAG_ELF_LIBC6;
 	}
     }