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
+ . */
+
+#ifndef _DL_IFUNC_CTRL_H
+#define _DL_IFUNC_CTRL_H 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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
#include
#include
+#include
#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
+ . */
+
+#ifndef _SYS_DLIFNCTL_H
+#define _SYS_DLIFNCTL_H
+
+#include
+
+#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