[RFC,2/2] Update prototype of IFUNC resolver for MIPS

Message ID 55E8C3C5.8000107@imgtec.com
State New, archived
Headers

Commit Message

Faraz Shahbazker Sept. 3, 2015, 10:03 p.m. UTC
  Comments are invited on the prototype of the IFUNC resolver for MIPS. We want
something flexible enough to handle current requirements and extensions.

This patch, from RobertS, provides the resolver with 2 HWCAP registers and a
callback control function. The callback currently provides the ability to
toggle/switch between FP ABI modes.

Caveat: specific HWCAP values for MIPS are not in place yet.

ChangeLog:

* sysdeps/mips/Makefile
	(sysdep_headers): Add sys/dlifnctl.h

* sysdeps/mips/dl-ifunc-ctrl.h: New file.
	(dl_ifunc_control): New function.

* sysdeps/mips/dl-irel.h
	Include dl-ifunc-ctrl.h
	Change resolver prototype to use 2 HWCAPs and dl_ifunc_control
	callback.

* sysdeps/mips/dl-machine-reject-phdr.h
	(REJECT): Replace _dl_debug_printf with GLRO(dl_debug_printf)
	(cached_fpabi_reject_phdr_p): Restricted to SHARED build.
	(ifunc_fpabi): New variable, track the first fpabi request.
	(dl_reject_fpabi_req): New function with using compat logic from
	elf_machine_reject_phdr_p.
	(elf_machine_reject_phdr_p): Refactored to call
	dl_reject_fpabi_req; all logic related to compatibility check
	moved to dl_reject_fpabi_req.  Replace _dl_debug_printf with
	GLRO(dl_debug_printf).

* sysdeps/mips/sys/dlifnctl.h
	(R_MIPS_IRELATIVE): New file.
	(DL_IFUNC_MIPS_FR_MODE_SWITCH DL_IFUNC_MIPS_FP_ABI): New operation
	request modes for IFUNC control callback.
---
 sysdeps/mips/Makefile                 |    2 +-
 sysdeps/mips/dl-ifunc-ctrl.h          |   90 ++++++++++++++++++++++
 sysdeps/mips/dl-irel.h                |    5 +-
 sysdeps/mips/dl-machine-reject-phdr.h |  131 +++++++++++++++++++++++----------
 sysdeps/mips/sys/dlifnctl.h           |   26 +++++++
 5 files changed, 214 insertions(+), 40 deletions(-)
 create mode 100644 sysdeps/mips/dl-ifunc-ctrl.h
 create mode 100644 sysdeps/mips/sys/dlifnctl.h
  

Comments

Joseph Myers Sept. 3, 2015, 10:14 p.m. UTC | #1
On Thu, 3 Sep 2015, Faraz Shahbazker wrote:

> Comments are invited on the prototype of the IFUNC resolver for MIPS. We want
> something flexible enough to handle current requirements and extensions.
> 
> This patch, from RobertS, provides the resolver with 2 HWCAP registers and a
> callback control function. The callback currently provides the ability to
> toggle/switch between FP ABI modes.

Please give a much more detailed explanation of the background to and 
motivation for this patch - why you need something that doesn't appear to 
be present for any other architecture.

>  sysdep_headers += regdef.h fpregdef.h sys/regdef.h sys/fpregdef.h \
> -		  sys/asm.h sgidefs.h
> +		  sys/asm.h sys/dlifnctl.h sgidefs.h

If you're adding a new API through a public header, it needs documenting 
in the user manual.
  
Szabolcs Nagy Sept. 4, 2015, 1:17 p.m. UTC | #2
On 03/09/15 23:03, Faraz Shahbazker wrote:
> Comments are invited on the prototype of the IFUNC resolver for MIPS. We want
> something flexible enough to handle current requirements and extensions.
>
> This patch, from RobertS, provides the resolver with 2 HWCAP registers and a
> callback control function. The callback currently provides the ability to
> toggle/switch between FP ABI modes.
...
> @@ -43,7 +44,9 @@ elf_ifunc_invoke (ElfW(Addr) addr)
>                               (unsigned long int)t_addr);
>       }
>
> -  return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
> +  return ((ElfW(Addr) (*) (unsigned long int, unsigned long int,
> +                          int (int, int))) addr)
> +    (GLRO(dl_hwcap), GLRO(dl_hwcap2), dl_ifunc_control);
>   }
>

the only ifunc documentation i know of says

"The calling convention of the STT_GNU_IFUNC function, which takes
no arguments and returns a function pointer, should follow the
processor-specific ABI."
(ifunc.txt at https://sites.google.com/site/x32abi/documents )

this is for x86 and wrong for other archs.

i think the interface contracts of the resolver should be
documented if ifunc is used outside the libc.

 > +static int __attribute_used__
 > +dl_ifunc_control (int operation, int value)
 > +{
...
 > +          /* Remember the choice for the last compabitle FP ABI.  */
 > +          ifunc_fpabi = in_abi;
...
> +/* FP ABI requirement for ifunc with callback.  The new mode switch can only
> +   be requested once.  */
> +
> +static int ifunc_fpabi = -1;
> +

i think the resolver needs to be as-safe, so modified
objects should be volatile (sig_atomic_t).

the ifunc situation is quite messy, is it supposed to be
used outside of the libc? (i know on x86, gcc target libs
and function multi-versioning use it, but i'd assume other
targets would move away from it outside the libc).
  
Ondrej Bilka Sept. 5, 2015, 5:32 a.m. UTC | #3
On Fri, Sep 04, 2015 at 02:17:56PM +0100, Szabolcs Nagy wrote:
> On 03/09/15 23:03, Faraz Shahbazker wrote:
> > +static int __attribute_used__
> > +dl_ifunc_control (int operation, int value)
> > +{
> ...
> > +          /* Remember the choice for the last compabitle FP ABI.  */
> > +          ifunc_fpabi = in_abi;
> ...

also typo here

> >+/* FP ABI requirement for ifunc with callback.  The new mode switch can only
> >+   be requested once.  */
> >+
> >+static int ifunc_fpabi = -1;
> >+
> 
> i think the resolver needs to be as-safe, so modified
> objects should be volatile (sig_atomic_t).
> 
We talked about this at cauldron. This is collorary of my question if
resolver is ok. We use recursive lock around that so I asked if its ok
when singal arrives while we hold it. Carlos said that we should check
all architectures if everything stays consistent. So ifunc should also
do same.

There is also question if we should resolve ifuncs early like with
LD_BIND_NOW=1. That we could already do eager binding implies that ifunc
should return same result while application runs.

> the ifunc situation is quite messy, is it supposed to be
> used outside of the libc? (i know on x86, gcc target libs
> and function multi-versioning use it, but i'd assume other
> targets would move away from it outside the libc).

Yes, its public but there isn't clear whats allowed and what isn't.
  
Matthew Fortune Sept. 14, 2015, 2:25 p.m. UTC | #4
Joseph Myers <joseph@codesourcery.com> writes:
> On Thu, 3 Sep 2015, Faraz Shahbazker wrote:
> 
> > Comments are invited on the prototype of the IFUNC resolver for MIPS.
> > We want something flexible enough to handle current requirements and
> extensions.
> >
> > This patch, from RobertS, provides the resolver with 2 HWCAP registers
> > and a callback control function. The callback currently provides the
> > ability to toggle/switch between FP ABI modes.
> 
> Please give a much more detailed explanation of the background to and
> motivation for this patch - why you need something that doesn't appear
> to be present for any other architecture.

I don't think there is much to debate over whether MIPS ifunc resolvers
take the hwcaps as arguments. That is common with other architectures
and it seems sensible to pass hwcap and hwcap2 for MIPS given we have
such wide variation in hardware. We will be populating hwcaps as soon
as possible for those who know we don't currently define any.

The unusual extension is the idea of a callback into the dynamic linker.
The origin of this comes from the MIPS FR0/FR1 interlinking plan which is
frequently referred to as the 'FPXX' ABI extension. The specific issue
is whether or not o32 software can rely on FR=1 mode i.e. FP64. There is
one sure fire way to do this and that is to build your module as FP64
using -mfp64 and the rest is already taken care of. However, in an ifunc
world we need the object(s) that contain ifunc resolvers and routines to
be marked with as few requirements as possible so that they will load on
the oldest/least feature-full hardware we want to support. This means
that we get no guarantee of what hardware mode is set nor whether the
mode is guaranteed not to be changed.

The primary reason for needing FR=1 is for MSA to be usable. Simply
seeing that MSA is available via a HWCAP is not enough as we have to
tell the dynamic linker to lock itself into FR=1/FP64 mode while the
ifunc's module is loaded. In fact it may not even be possible to enter
FR=1 mode because of the modules which are already loaded.

The callback interface is proposed as relatively generic to support
making different kinds of requests. The one to support MIPS FP modes is
to basically request that the current module has its FP ABI upgraded
to a specific value and report if that succeeds. I haven't reviewed the
detailed implementation of this yet but that is the general goal.

I hope that at least covers the basics of why this feature is being
proposed.

Thanks,
Matthew
  
Matthew Fortune Sept. 14, 2015, 2:33 p.m. UTC | #5
Ondřej Bílka <neleai@seznam.cz>
> > i think the resolver needs to be as-safe, so modified objects should
> > be volatile (sig_atomic_t).
> >
> We talked about this at cauldron. This is collorary of my question if
> resolver is ok. We use recursive lock around that so I asked if its ok
> when singal arrives while we hold it. Carlos said that we should check
> all architectures if everything stays consistent. So ifunc should also
> do same.
> 
> There is also question if we should resolve ifuncs early like with
> LD_BIND_NOW=1. That we could already do eager binding implies that ifunc
> should return same result while application runs.

I'm not sure I see any specific need for the outcome of an ifunc to be the
same when resolved early/late. Apart from the resolved function needing to
be at least usable I don't think there has to be any further requirements. 

> > the ifunc situation is quite messy, is it supposed to be used outside
> > of the libc? (i know on x86, gcc target libs and function
> > multi-versioning use it, but i'd assume other targets would move away
> > from it outside the libc).
> 
> Yes, its public but there isn't clear whats allowed and what isn't.

Are you asking whether ifunc should be usable by an ordinary programmer in
their library? I'd say that it absolutely should be usable. We have some
syntactic sugar in GCC to simplify defining ifuncs and for those who do
then they need to follow a few rules about what can and cannot be done in
a resolver. You could go as far as to say that the idea proposed in this
patch could be the only way for an ifunc resolver to do anything other than
plain C code. i.e. no library calls allowed but some services are exposed
via a dynamic linker callback.

Thanks,
Matthew
  
Joseph Myers Sept. 14, 2015, 3:56 p.m. UTC | #6
On Mon, 14 Sep 2015, Matthew Fortune wrote:

> The primary reason for needing FR=1 is for MSA to be usable. Simply
> seeing that MSA is available via a HWCAP is not enough as we have to
> tell the dynamic linker to lock itself into FR=1/FP64 mode while the
> ifunc's module is loaded. In fact it may not even be possible to enter
> FR=1 mode because of the modules which are already loaded.

And it's not possible to have a pseudo-HWCAP bit (with a reservation so 
the kernel won't allocate it) that means "can use FP64 mode", or the IFUNC 
can't switch to FP64 mode using existing public interfaces so having such 
a bit wouldn't be sufficient?

I think the "GNU ifunc - work in progress please ignore" sections of 
<https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking> 
need expanding to explain the proposed ABI here.
  
Szabolcs Nagy Sept. 14, 2015, 4:39 p.m. UTC | #7
On 14/09/15 15:33, Matthew Fortune wrote:
> Ondřej Bílka <neleai@seznam.cz>
>>> the ifunc situation is quite messy, is it supposed to be used outside
>>> of the libc? (i know on x86, gcc target libs and function
>>> multi-versioning use it, but i'd assume other targets would move away
>>> from it outside the libc).
>>
>> Yes, its public but there isn't clear whats allowed and what isn't.
>
> Are you asking whether ifunc should be usable by an ordinary programmer in
> their library? I'd say that it absolutely should be usable. We have some

yes

> syntactic sugar in GCC to simplify defining ifuncs and for those who do
> then they need to follow a few rules about what can and cannot be done in

i think the rules currently depend on dynamic
linker internals and are target specific.
(which is ok if ifunc is only used in the libc
but not ok if users write ifunc resolvers).

e.g. i don't see the fp registers saved/restored
in mips _dl_runtime_resolve, so the ifunc resolver
must not clobber fp (argument) regs.
(on x86 an ifunc resolver can clobber fp regs.)

> a resolver. You could go as far as to say that the idea proposed in this
> patch could be the only way for an ifunc resolver to do anything other than
> plain C code. i.e. no library calls allowed but some services are exposed
> via a dynamic linker callback.
>

if no library calls are allowed from a resolver
then the ifunc dispatch mechanism is limited,
if some libc calls are allowed then those should
be documented.

(some libc calls may be generated by the compiler
e.g. memcpy)

> Thanks,
> Matthew
>
  
Ondrej Bilka Sept. 15, 2015, 10:48 a.m. UTC | #8
On Mon, Sep 14, 2015 at 02:33:47PM +0000, Matthew Fortune wrote:
> Ondřej Bílka <neleai@seznam.cz>
> > > i think the resolver needs to be as-safe, so modified objects should
> > > be volatile (sig_atomic_t).
> > >
> > We talked about this at cauldron. This is collorary of my question if
> > resolver is ok. We use recursive lock around that so I asked if its ok
> > when singal arrives while we hold it. Carlos said that we should check
> > all architectures if everything stays consistent. So ifunc should also
> > do same.
> > 
> > There is also question if we should resolve ifuncs early like with
> > LD_BIND_NOW=1. That we could already do eager binding implies that ifunc
> > should return same result while application runs.
> 
> I'm not sure I see any specific need for the outcome of an ifunc to be the
> same when resolved early/late. Apart from the resolved function needing to
> be at least usable I don't think there has to be any further requirements. 
>
While not I am saying that user shouldn't rely that ifunc would be
resolved late. 
 
> > > the ifunc situation is quite messy, is it supposed to be used outside
> > > of the libc? (i know on x86, gcc target libs and function
> > > multi-versioning use it, but i'd assume other targets would move away
> > > from it outside the libc).
> > 
> > Yes, its public but there isn't clear whats allowed and what isn't.
> 
> Are you asking whether ifunc should be usable by an ordinary programmer in
> their library? 

I am not saying that, I am saying that currently one frequently finds
situations where it isnt clear if its user fault or our. For example I
wrote sample ifunc for shared library, it got seqfault and it turned out
it was because function that I returned was public and not resolved yet.

> I'd say that it absolutely should be usable. We have some
> syntactic sugar in GCC to simplify defining ifuncs and for those who do
> then they need to follow a few rules about what can and cannot be done in
> a resolver. You could go as far as to say that the idea proposed in this
> patch could be the only way for an ifunc resolver to do anything other than
> plain C code. i.e. no library calls allowed but some services are exposed
> via a dynamic linker callback.
>
I would disagree with that for two reason, first is that no library
calls limit usability. If one wants to select function based on profile
data he needs to mmap file with data or so.

Second is that ifunc should be implementation detail rather than
interface. I have on my todo list to add multiple cpu dispatch for gcc
and there are several ways to do it. I would like more fat binaries as
there is problem that to inline ifunc you need to also turn callers into
ifunc so gcc would need to understand resolver.

Other alternative that uses ifunc would add gcc attribute that tells gcc
to make variants of function for each arch, that could be now done with
macro that defines resolver, for variants uses __attribute__((target("foo"))) 
and always inline original function.
  

Patch

diff --git a/sysdeps/mips/Makefile b/sysdeps/mips/Makefile
index 463b121..a598b57 100644
--- a/sysdeps/mips/Makefile
+++ b/sysdeps/mips/Makefile
@@ -1,6 +1,6 @@ 
 ifeq ($(subdir),misc)
 sysdep_headers += regdef.h fpregdef.h sys/regdef.h sys/fpregdef.h \
-		  sys/asm.h sgidefs.h
+		  sys/asm.h sys/dlifnctl.h sgidefs.h
 endif
 
 ifeq ($(subdir),setjmp)
diff --git a/sysdeps/mips/dl-ifunc-ctrl.h b/sysdeps/mips/dl-ifunc-ctrl.h
new file mode 100644
index 0000000..daa95b3
--- /dev/null
+++ b/sysdeps/mips/dl-ifunc-ctrl.h
@@ -0,0 +1,90 @@ 
+/* Machine-dependent callback control function for ifunc resolver functions.
+   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_IFUNC_CTRL_H
+#define _DL_IFUNC_CTRL_H 1
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sgidefs.h>
+#include <link.h>
+#include <elf.h>
+#include <ldsodefs.h>
+#include <sys/dlifnctl.h>
+#include <dl-machine-reject-phdr.h>
+
+static int __attribute_used__
+dl_ifunc_control (int operation, int value)
+{
+  int in_abi = -1;
+
+  switch (operation)
+    {
+    case DL_IFUNC_MIPS_FR_MODE_SWITCH:
+      if (value == 0)
+	in_abi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+      else if (value == 1)
+	in_abi = Val_GNU_MIPS_ABI_FP_64;
+      else
+	{
+	  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS))
+	    GLRO(dl_debug_printf) ("  requested invalid FR mode: 0x%x\n",
+				   value);
+#if _MIPS_SIM == _ABIO32
+# if HAVE_PRCTL_FP_MODE
+	  return __prctl (PR_GET_FP_MODE);
+# else
+	  /* If the PR_GET_FP_MODE is not supported then only FR0
+	     is available.  */
+	  return 0;
+# endif
+#else
+	  return 1;
+#endif
+	}
+      /* Fall through.  */
+    case DL_IFUNC_MIPS_FP_ABI:
+      if (in_abi == -1)
+	in_abi = value;
+
+      /* Try the new requirement as if it was an object. */
+      if (!dl_reject_fpabi_req (in_abi))
+	{
+	   /* Remember the choice for the last compabitle FP ABI.  */
+	   ifunc_fpabi = in_abi;
+	}
+#if _MIPS_SIM == _ABIO32
+# if HAVE_PRCTL_FP_MODE
+      return __prctl (PR_GET_FP_MODE);
+# else
+      return 0;
+# endif
+#else
+      return 1;
+#endif
+    default:
+#ifdef SHARED
+      GLRO(dl_debug_printf) ("unrecognized ifunc operation\n");
+      _exit (127);
+#else
+      _dl_fatal_printf ("unrecognized ifunc operation in static binary\n");
+#endif
+      /* NOT REACHED */
+    }
+}
+#endif
diff --git a/sysdeps/mips/dl-irel.h b/sysdeps/mips/dl-irel.h
index 47f3257..2a42380 100644
--- a/sysdeps/mips/dl-irel.h
+++ b/sysdeps/mips/dl-irel.h
@@ -26,6 +26,7 @@ 
 #include <link.h>
 #include <elf.h>
 #include <ldsodefs.h>
+#include <dl-ifunc-ctrl.h>
 
 #define ELF_MACHINE_IREL	1
 
@@ -43,7 +44,9 @@  elf_ifunc_invoke (ElfW(Addr) addr)
 			     (unsigned long int)t_addr);
     }
 
-  return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+  return ((ElfW(Addr) (*) (unsigned long int, unsigned long int,
+			   int (int, int))) addr)
+    (GLRO(dl_hwcap), GLRO(dl_hwcap2), dl_ifunc_control);
 }
 
 /* Allow either R_MIPS_RELATIVE or the nop R_MIPS_NONE.  */
diff --git a/sysdeps/mips/dl-machine-reject-phdr.h b/sysdeps/mips/dl-machine-reject-phdr.h
index b09df30..6233fae 100644
--- a/sysdeps/mips/dl-machine-reject-phdr.h
+++ b/sysdeps/mips/dl-machine-reject-phdr.h
@@ -32,7 +32,7 @@ 
 #define REJECT(str, args...)						      \
   {									      \
     if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))		      \
-      _dl_debug_printf (str, ##args);					      \
+      GLRO(dl_debug_printf) (str, ##args);				      \
     return true;							      \
   }
 
@@ -49,6 +49,7 @@  find_mips_abiflags (const ElfW(Phdr) *phdr, ElfW(Half) phnum)
   return NULL;
 }
 
+#ifdef SHARED
 /* Cache the FP ABI value from the PT_MIPS_ABIFLAGS program header.  */
 
 static bool
@@ -82,6 +83,7 @@  cached_fpabi_reject_phdr_p (struct link_map *l)
     }
   return false;
 }
+#endif
 
 /* Return a description of the specified floating-point ABI.  */
 
@@ -145,23 +147,22 @@  static const struct abi_req reqs[Val_GNU_MIPS_ABI_FP_MAX + 1] =
 
 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.  */
+/* FP ABI requirement for ifunc with callback.  The new mode switch can only
+   be requested once.  */
+
+static int ifunc_fpabi = -1;
+
+/* Return true iff that the new FP ABI requirement conflicts with any
+   currently loaded object.  */
 
 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)
+dl_reject_fpabi_req (int in_abi)
 {
-  const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum);
+#ifdef SHARED
   struct link_map *l;
   Lmid_t nsid;
-  int in_abi = -1;
+#endif
   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;
@@ -173,30 +174,6 @@  elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
 # 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);
 
@@ -207,6 +184,7 @@  elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
   /* Obtain the initial requirements.  */
   in_req = (in_abi == -1) ? none_req : reqs[in_abi];
 
+#ifdef SHARED
   /* Check that the new requirement does not conflict with any currently
      loaded object.  */
   for (nsid = 0; nsid < DL_NNS; ++nsid)
@@ -266,6 +244,37 @@  elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
 		  fpabi_string (in_abi),
 		  fpabi_string (l->l_mach.fpabi));
       }
+#endif
+
+  /* Check the compability of the FP ABI requested in ifunc callback with
+     the loaded objects.  */
+  if (ifunc_fpabi != -1)
+    {
+      struct abi_req existing_req;
+
+      /* Found a perfect match, success.  */
+      perfect_match |= (in_abi == ifunc_fpabi);
+
+      existing_req = reqs[ifunc_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)
+	{
+	}
+      else
+	REJECT ("   uses %s, ifunc already loaded %s\n",
+		fpabi_string (in_abi),
+		fpabi_string (ifunc_fpabi));
+    }
 
 #if _MIPS_SIM == _ABIO32
   /* At this point we know that the newly loaded object is compatible with all
@@ -274,8 +283,9 @@  elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
       && !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");
+	GLRO (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
@@ -323,4 +333,49 @@  elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
   return false;
 }
 
+/* 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);
+  int in_abi = -1;
+  Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL;
+
+  /* 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;
+    }
+
+  if (dl_reject_fpabi_req (in_abi))
+    return true;
+
+  return false;
+}
+
 #endif /* dl-machine-reject-phdr.h */
diff --git a/sysdeps/mips/sys/dlifnctl.h b/sysdeps/mips/sys/dlifnctl.h
new file mode 100644
index 0000000..6e21fcf
--- /dev/null
+++ b/sysdeps/mips/sys/dlifnctl.h
@@ -0,0 +1,26 @@ 
+/* 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 _SYS_DLIFNCTL_H
+#define _SYS_DLIFNCTL_H
+
+#include <elf.h>
+
+#define DL_IFUNC_MIPS_FR_MODE_SWITCH  0 /* Request FR mode change.  */
+#define DL_IFUNC_MIPS_FP_ABI	      1 /* Request FR mode for FP ABI.  */
+
+#endif