This is the mail archive of the libc-alpha@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]

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


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

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
-- 
1.7.9.5


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