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, PATCH, MIPS] Add support for O32 FPXX and program header based ABI information


Hi Joseph,

Attached is an initial patch to support the O32 FPXX ABI as described here:

https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking

This has been discussed at length on GCC and binutils mailing lists and has
approval in principle from MIPS kernel developers at Imagination.

The proposed ABI was initially reviewed/discussed on the GCC mailing
list starting from the following posts:

http://gcc.gnu.org/ml/gcc/2014-02/msg00415.html
http://gcc.gnu.org/ml/gcc/2014-03/msg00191.html

Then refined up to the current spec on binutils with the latest patch here.

http://sourceware.org/ml/binutils/2014-05/msg00002.html

To implement this feature I need an additional hook during the loading of
objects to allow a target to inspect the headers (and segments they point
at) to decide if the library is compatible or not. I have made a simple
attempt at this but am quite willing to adapt to better suggestions.

The current implementation tracks the individual ABI requirements of each
loaded module and caches the information so that program headers are only
inspected once per library. This provides the greatest control but could
theoretically be reduced to simply recording the current state of a process
with a single global value. Since this is an implementation detail this too
is open to debate.

I think I need to do something relating to caching objects but quite
honestly have no idea on that front. I've used the work from maciej on
NAN2008 handling as a reference for the areas I need to modify for this
implementation but haven't figured that part out.

I'm also not entirely sure how to go about writing tests for this in glibc
but will look through the test framework to see if anything is possible. I
have attached a basic test that allows all combinations of O32 FP32,
O32 FPXX and O32 FP64 modules to be loaded and tested manually. I have a
hacked patch for qemu to read the new PT_MIPS_ABIFLAGS which I have been
using to test this; I can make that available but will need to rebase to
a public qemu release.

The corresponding patch to implement O32 FPXX in GCC will hopefully be
posted tomorrow.

Regards,
Matthew

2014-05-01  Matthew Fortune  <matthew.fortune@imgtec.com>

    * config.h/in (HAVE_MIPS_MODULE_FPXX_DIRECTIVE): Undefine.
    * elf/dl-load.c (open_verify): Add optional hook for phdr check.
    * elf/elf.h (PT_MIPS_ABIFLAGS): Define.
    (Elf_ABIFlags_v0): New structure.
    (AFL_REG_NONE, AFL_REG_32, AFL_REG_64, AFL_REG_128): Define.
    (AFL_ASE_DSP, AFL_ASE_DSP64, AFL_ASE_DSPR2, AFL_ASE_EVA): Likewise.
    (AFL_ASE_MCU, AFL_ASE_MDMX, AFL_ASE_MIPS3D, AFL_ASE_MT): Likewise.
    (AFL_ASE_SMARTMIPS, AFL_ASE_VIRT, AFL_ASE_VIRT64, AFL_ASE_MSA): Likewise.
    (AFL_ASE_MSA64, AFL_ASE_MIPS16, AFL_ASE_MICROMIPS): Likewise.
    (AFL_EXT_XLR, AFL_EXT_OCTEON2, AFL_EXT_OCTEONP): Likewise.
    (AFL_EXT_LOONGSON_3A, AFL_EXT_OCTEON, AFL_EXT_5900): Likewise.
    (AFL_EXT_4010, AFL_EXT_4100, AFL_EXT_3900, AFL_EXT_10000): Likewise.
    (AFL_EXT_SB1, AFL_EXT_4111, AFL_EXT_4120, AFL_EXT_5400): Likewise.
    (AFL_EXT_5500, AFL_EXT_LOONGSON_2E, 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): Likewise.
    * sysdeps/mips/bits/hwcap.h: New file.
    * sysdeps/mips/bits/linkmap.h (struct link_map_machine): Add fpmode
    field.
    * sysdeps/mips/dl-machine.h: Include unistd.h.
    (ELF_MACHINE_NEEDS_PHDR_CHECK): Define.
    (find_mips_abiflags): New static inline function.
    (switch_frmode_to): New static no-inline function.
    (mips_fp_abi, elf_machine_phdr_check): new static function.
    * sysdeps/mips/dl-procinfo.c (_dl_mips_cap_flags): Declare.
    * sysdeps/mips/dl-procinfo.h (_DL_HWCAP_COUNT): Define.
    (HWCAP_IMPORTANT): Define.
    (_dl_procinfo): New static inline function.
    (_dl_hwcap_string, _dl_string_hwcap): Likewise.
    * sysdeps/unix/mips/sysdep.h (_SYS_AUXV_H): Define.
    (bits/hwcap.h): Include.
    * sysdeps/unix/sysv/linux/mips/configure.ac: Check for .module fpxx
    (HAVE_MIPS_MODULE_FPXX_DIRECTIVE): Define.
    * sysdeps/unix/sysv/linux/mips/configure: Regenerate.
    * sysdeps/unix/sysv/linux/mips/getcontext.S
    (HAVE_MIPS_MODULE_FPXX_DIRECTIVE): Use.
    * sysdeps/unix/sysv/linux/mips/setcontext.S: Likewise.
    * sysdeps/unix/sysv/linux/mips/swapcontext.S: Likewise.
---
 config.h.in                                |    6 +-
 elf/dl-load.c                              |    7 +
 elf/elf.h                                  |   97 ++++++++++++-
 sysdeps/mips/bits/hwcap.h                  |   23 +++
 sysdeps/mips/bits/linkmap.h                |    1 +
 sysdeps/mips/dl-machine.h                  |  218 ++++++++++++++++++++++++++++
 sysdeps/mips/dl-procinfo.c                 |   16 ++
 sysdeps/mips/dl-procinfo.h                 |   50 +++++-
 sysdeps/unix/mips/sysdep.h                 |    3 +
 sysdeps/unix/sysv/linux/mips/configure     |   27 ++++
 sysdeps/unix/sysv/linux/mips/configure.ac  |   15 ++
 sysdeps/unix/sysv/linux/mips/getcontext.S  |    4 +
 sysdeps/unix/sysv/linux/mips/setcontext.S  |    4 +
 sysdeps/unix/sysv/linux/mips/swapcontext.S |    4 +
 14 files changed, 462 insertions(+), 13 deletions(-)
 create mode 100644 sysdeps/mips/bits/hwcap.h

diff --git a/config.h.in b/config.h.in
index 40797e7..05da98a 100644
--- a/config.h.in
+++ b/config.h.in
@@ -241,8 +241,12 @@
 /* The pt_chown binary is being built and used by grantpt.  */
 #undef HAVE_PT_CHOWN
 
-/* ports/sysdeps/mips/configure.in  */
+/* sysdeps/mips/configure.in  */
 /* Define if using the IEEE 754-2008 NaN encoding on the MIPS target.  */
 #undef HAVE_MIPS_NAN2008
 
+/* sysdeps/unix/sysv/linux/mips/configure.in  */
+/* Define if the assembler supports the .module fp=xx directive.  */
+#undef HAVE_MIPS_MODULE_FPXX_DIRECTIVE
+
 #endif
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 6501ff2..43aacdc 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1866,6 +1866,13 @@ open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
 	    }
 	}
 
+#ifdef ELF_MACHINE_NEEDS_PHDR_CHECK
+      if (!__builtin_expect (
+	     elf_machine_phdr_check (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
+				     fd, loader), 1))
+	goto close_and_out;
+#endif
+
       /* Check .note.ABI-tag if present.  */
       for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph)
 	if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4)
diff --git a/elf/elf.h b/elf/elf.h
index 40e87b2..dccdee9 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1631,9 +1631,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 +1756,96 @@ 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;
+  /* Mask of processor-specific extensions.  */
+  Elf32_Word isa_ext;
+  /* Mask of ASEs used.  */
+  Elf32_Word ases;
+  /* Mask of general flags.  */
+  Elf32_Word flags1;
+  Elf32_Word flags2;
+} Elf_ABIFlags_v0;
+
+/* Values for the register size bytes of an abi flags structure.  */
+
+#define AFL_REG_NONE	      0x00	 /* No registers.  */
+#define AFL_REG_32	      0x01	 /* 32-bit registers.  */
+#define AFL_REG_64	      0x02	 /* 64-bit registers.  */
+#define AFL_REG_128	      0x03	 /* 128-bit registers.  */
+
+/* Masks for the ases word of an ABI flags structure.  */
+
+#define AFL_ASE_DSP	      0x00000001 /* DSP ASE.  */
+#define AFL_ASE_DSP64	      0x00000002 /* DSP ASE (64-bit).  */
+#define AFL_ASE_DSPR2	      0x00000004 /* DSP R2 ASE.  */
+#define AFL_ASE_EVA	      0x00000008 /* Enhanced VA Scheme.  */
+#define AFL_ASE_MCU	      0x00000010 /* MCU (MicroController) ASE.  */
+#define AFL_ASE_MDMX	      0x00000020 /* MDMX ASE.  */
+#define AFL_ASE_MIPS3D	      0x00000040 /* MIPS-3D ASE.  */
+#define AFL_ASE_MT	      0x00000080 /* MT ASE.  */
+#define AFL_ASE_SMARTMIPS     0x00000100 /* SmartMIPS ASE.  */
+#define AFL_ASE_VIRT	      0x00000200 /* VZ ASE.  */
+#define AFL_ASE_VIRT64	      0x00000400 /* VZ ASE (64-bit).  */
+#define AFL_ASE_MSA	      0x00000800 /* MSA ASE.  */
+#define AFL_ASE_MSA64	      0x00001000 /* MSA ASE (64-bit).  */
+#define AFL_ASE_MIPS16	      0x00002000 /* MIPS16 ASE.  */
+#define AFL_ASE_MICROMIPS     0x00004000 /* MICROMIPS ASE.  */
+#define AFL_ASE_XPA	      0x00002000 /* XPA ASE.  */
+
+/* Masks for the isa_ext word of an ABI flags structure.  */
+
+#define AFL_EXT_XLR	      0x00000020 /* RMI Xlr instruction.  */
+#define AFL_EXT_OCTEON2	      0x00000100 /* Cavium Networks Octeon2.  */
+#define AFL_EXT_OCTEONP	      0x00000200 /* Cavium Networks OcteonP.  */
+#define AFL_EXT_LOONGSON_3A   0x00000400 /* Loongson 3A.  */
+#define AFL_EXT_OCTEON	      0x00000800 /* Cavium Networks Octeon.  */
+#define AFL_EXT_5900	      0x00004000 /* MIPS R5900 instruction.  */
+#define AFL_EXT_4650	      0x00010000 /* MIPS R4650 instruction.  */
+#define AFL_EXT_4010	      0x00020000 /* LSI R4010 instruction.  */
+#define AFL_EXT_4100	      0x00040000 /* NEC VR4100 instruction.  */
+#define AFL_EXT_3900	      0x00080000 /* Toshiba R3900 instruction.  */
+#define AFL_EXT_10000	      0x00100000 /* MIPS R10000 instruction.  */
+#define AFL_EXT_SB1	      0x00200000 /* Broadcom SB-1 instruction.  */
+#define AFL_EXT_4111	      0x00400000 /* NEC VR4111/VR4181 instruction.  */
+#define AFL_EXT_4120	      0x00800000 /* NEC VR4120 instruction.  */
+#define AFL_EXT_5400	      0x01000000 /* NEC VR5400 instruction.  */
+#define AFL_EXT_5500	      0x02000000 /* NEC VR5500 instruction.  */
+#define AFL_EXT_LOONGSON_2E   0x40000000 /* ST Micro Loongson 2E.  */
+#define AFL_EXT_LOONGSON_2F   0x80000000 /* ST Micro Loongson 2F.  */
+
+/* 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
+};
 
 /* HPPA specific definitions.  */
 
diff --git a/sysdeps/mips/bits/hwcap.h b/sysdeps/mips/bits/hwcap.h
new file mode 100644
index 0000000..96575d2
--- /dev/null
+++ b/sysdeps/mips/bits/hwcap.h
@@ -0,0 +1,23 @@
+/* Defines for bits in AT_HWCAP.
+   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 _SYS_AUXV_H
+# error "Never include <bits/hwcap.h> directly; use <sys/auxv.h> instead."
+#endif
+
+#define HWCAP_MIPS_UFR	0x00000001
diff --git a/sysdeps/mips/bits/linkmap.h b/sysdeps/mips/bits/linkmap.h
index a6df782..dfbc3be 100644
--- a/sysdeps/mips/bits/linkmap.h
+++ b/sysdeps/mips/bits/linkmap.h
@@ -1,4 +1,5 @@
 struct link_map_machine
   {
     ElfW(Addr) plt; /* Address of .plt */
+    ElfW(Word) fpmode; /* Overall FP mode */
   };
diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h
index 5a07c0b..b10c228 100644
--- a/sysdeps/mips/dl-machine.h
+++ b/sysdeps/mips/dl-machine.h
@@ -32,6 +32,7 @@
 #include <sgidefs.h>
 #include <sys/asm.h>
 #include <dl-tls.h>
+#include <unistd.h>
 
 /* The offset of gp from GOT might be system-dependent.  It's set by
    ld.  The same value is also */
@@ -107,6 +108,223 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
     }
 }
 
+#define ELF_MACHINE_NEEDS_PHDR_CHECK
+
+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;
+}
+
+#if _MIPS_SIM == _ABIO32
+/* Modify the mode of the floating-point registers.  This function must not
+   be inlined as it relies on the calling-convention to only need to save
+   restore the callee-saved registers around the mode switch.  */
+
+static __attribute__ ((noinline)) void
+switch_frmode_to (int newmode)
+{
+  asm volatile
+      ("addu $sp, $sp, -48\n"
+       "sdc1 $f20, 0($sp)\n"
+       "sdc1 $f22, 8($sp)\n"
+       "sdc1 $f24, 16($sp)\n"
+       "sdc1 $f26, 24($sp)\n"
+       "sdc1 $f28, 32($sp)\n"
+       "sdc1 $f30, 40($sp)\n"
+       "beq %0, $0, 1f\n"
+       "ctc1 $0, $4\n"
+       "b 2f\n"
+       "1:\n"
+       "ctc1 $0, $1\n"
+       "2:\n"
+       "ldc1 $f20, 0($sp)\n"
+       "ldc1 $f22, 8($sp)\n"
+       "ldc1 $f24, 16($sp)\n"
+       "ldc1 $f26, 24($sp)\n"
+       "ldc1 $f28, 32($sp)\n"
+       "ldc1 $f30, 40($sp)\n"
+       "addu $sp, $sp, -48\n" :: "r"(newmode));
+}
+#endif
+
+static const char *
+mips_fp_abi (int val)
+{
+  switch (val)
+    {
+    case Val_GNU_MIPS_ABI_FP_ANY:
+      return "no float";
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      return "Hard float (DP)";
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      return "Hard float (SP)";
+    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)";
+    default:
+      return "Unknown FP ABI";
+    }
+}
+
+static int __attribute_used__
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum);
+  struct link_map *l;
+  Lmid_t nsid;
+  int req_abi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+  Elf_ABIFlags_v0 *mips_abiflags = NULL;
+
+  /* Read the attributes section.  */
+  if (ph != NULL)
+    {
+      ElfW(Addr) size = ph->p_filesz;
+
+      if (ph->p_offset + size <= (size_t) len)
+	mips_abiflags = (Elf_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)
+	    {
+	      if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+		_dl_debug_printf ("   Unable to read PT_MIPS_ABIFLAGS\n");
+	      return 0;
+	    }
+	}
+      if (size < sizeof (Elf_ABIFlags_v0))
+	{
+	  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+	    _dl_debug_printf ("   contains malformed PT_MIPS_ABIFLAGS\n");
+	  return 0;
+	}
+      req_abi = mips_abiflags->fp_abi;
+    }
+
+  /* ANY is compatible with anything.  */
+  if (req_abi == Val_GNU_MIPS_ABI_FP_ANY)
+    return 1;
+
+  /* Check that the new mode 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)
+      {
+	bool success = true;
+	if (l->l_mach.fpmode == 0)
+	  {
+	    l->l_mach.fpmode = Val_GNU_MIPS_ABI_FP_DOUBLE;
+	    ph = find_mips_abiflags (l->l_phdr, l->l_phnum);
+	    if (ph)
+	      {
+		if (ph->p_filesz < sizeof (Elf_ABIFlags_v0))
+		  {
+		    if (__builtin_expect (GLRO(dl_debug_mask)
+					  & DL_DEBUG_LIBS, 0))
+		      _dl_debug_printf ("   malformed PT_MIPS_ABIFLAGS found\n");
+		    return 0;
+		  }
+
+		mips_abiflags = (Elf_ABIFlags_v0 *) (l->l_addr + ph->p_vaddr);
+		l->l_mach.fpmode = mips_abiflags->fp_abi;
+	      }
+	  }
+	switch (req_abi)
+	  {
+	  case Val_GNU_MIPS_ABI_FP_ANY:
+	    /* Can't happen, see above.  */
+	    break;
+	  case Val_GNU_MIPS_ABI_FP_DOUBLE:
+	    if (l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_DOUBLE
+#if _MIPS_SIM == _ABIO32
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_XX
+#endif
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_ANY)
+	      success = false;
+	    break;
+	  case Val_GNU_MIPS_ABI_FP_SINGLE:
+	    if (l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_SINGLE
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_ANY)
+	      success = false;
+	    break;
+	  case Val_GNU_MIPS_ABI_FP_SOFT:
+	    if (l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_SOFT
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_ANY)
+	      success = false;
+	    break;
+#if _MIPS_SIM == _ABIO32
+	  case Val_GNU_MIPS_ABI_FP_XX:
+	    if (l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_XX
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_DOUBLE
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_64
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_ANY)
+	      success = false;
+	    break;
+	  case Val_GNU_MIPS_ABI_FP_64:
+	    if (l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_64
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_XX
+		&& l->l_mach.fpmode != Val_GNU_MIPS_ABI_FP_ANY)
+	      success = false;
+	    break;
+#endif
+	  default:
+	    success = false;
+	  }
+
+	if (!success)
+	  {
+	    if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+	      _dl_debug_printf ("   uses %s, already loaded %s\n",
+				mips_fp_abi (req_abi),
+				mips_fp_abi (l->l_mach.fpmode));
+	    return 0;
+	  }
+      }
+
+#if _MIPS_SIM == _ABIO32
+  if (req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE
+      || req_abi == Val_GNU_MIPS_ABI_FP_64)
+    {
+      if (GLRO(dl_hwcap) & HWCAP_MIPS_UFR)
+	{
+	  int status;
+	  /* Obtain UFR mode information.  */
+	  asm volatile ("cfc1 %0,$1\n": "=r"(status));
+	  if (status == 0 && req_abi == Val_GNU_MIPS_ABI_FP_64)
+	    switch_frmode_to (1);
+	  else if (status == 1 && req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE)
+	    switch_frmode_to (0);
+
+	  asm volatile ("cfc1 %0,$1\n": "=r"(status));
+	  if ((status == 0 && req_abi == Val_GNU_MIPS_ABI_FP_64)
+	      || (status == 1 && req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE))
+	    _dl_signal_error (0, map->l_name, NULL, "Unable to set FP mode");
+	}
+      else if (req_abi == Val_GNU_MIPS_ABI_FP_64)
+	{
+	  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+	    _dl_debug_printf ("   requires FR mode switch but UFR is not supported\n");
+	  return 0;
+	}
+#endif
+    }
+
+  return 1;
+}
+
 static inline ElfW(Addr) *
 elf_mips_got_from_gpreg (ElfW(Addr) gpreg)
 {
diff --git a/sysdeps/mips/dl-procinfo.c b/sysdeps/mips/dl-procinfo.c
index 4a3dbf3..6972b7c 100644
--- a/sysdeps/mips/dl-procinfo.c
+++ b/sysdeps/mips/dl-procinfo.c
@@ -59,5 +59,21 @@ PROCINFO_CLASS const char _dl_mips_platforms[4][11]
 ,
 #endif
 
+#if !defined PROCINFO_DECL && defined SHARED
+  ._dl_mips_cap_flags
+#else
+PROCINFO_CLASS const char _dl_mips_cap_flags[1][4]
+#endif
+#ifndef PROCINFO_DECL
+= {
+    "ufr"
+  }
+#endif
+#if !defined SHARED || defined PROCINFO_DECL
+;
+#else
+,
+#endif
+
 #undef PROCINFO_DECL
 #undef PROCINFO_CLASS
diff --git a/sysdeps/mips/dl-procinfo.h b/sysdeps/mips/dl-procinfo.h
index b2b7702..d50d8cf 100644
--- a/sysdeps/mips/dl-procinfo.h
+++ b/sysdeps/mips/dl-procinfo.h
@@ -50,18 +50,50 @@ _dl_string_platform (const char *str)
   return -1;
 };
 
-/* We cannot provide a general printing function.  */
-#define _dl_procinfo(type, word) -1
+#define _DL_HWCAP_COUNT	1
 
-/* There are no hardware capabilities defined.  */
-#define _dl_hwcap_string(idx) ""
+#define HWCAP_IMPORTANT         (HWCAP_MIPS_UFR)
 
-/* By default there is no important hardware capability.  */
-#define HWCAP_IMPORTANT (0)
+static inline int
+__attribute__ ((unused))
+_dl_procinfo (unsigned int type, unsigned long int word)
+{
+  int i;
+
+  /* Fallback to unknown output mechanism.  */
+  if (type == AT_HWCAP2)
+    return -1;
+
+  _dl_printf ("AT_HWCAP:   ");
+
+  for (i = 0; i < _DL_HWCAP_COUNT; ++i)
+    if (word & (1 << i))
+      _dl_printf (" %s", GLRO(dl_mips_cap_flags)[i]);
+
+  _dl_printf ("\n");
+
+  return 0;
+}
+
+static inline const char *
+__attribute__ ((unused))
+_dl_hwcap_string (int idx)
+{
+  return GLRO(dl_mips_cap_flags)[idx];
+};
 
-/* We don't have any hardware capabilities.  */
-#define _DL_HWCAP_COUNT	0
+static inline int
+__attribute__ ((unused))
+_dl_string_hwcap (const char *str)
+{
+  int i;
 
-#define _dl_string_hwcap(str) (-1)
+  for (i = 0; i < _DL_HWCAP_COUNT; i++)
+    {
+      if (strcmp (str, GLRO(dl_mips_cap_flags)[i]) == 0)
+	return i;
+    }
+  return -1;
+};
 
 #endif /* dl-procinfo.h */
diff --git a/sysdeps/unix/mips/sysdep.h b/sysdeps/unix/mips/sysdep.h
index d59fac0..7c930ef 100644
--- a/sysdeps/unix/mips/sysdep.h
+++ b/sysdeps/unix/mips/sysdep.h
@@ -19,6 +19,9 @@
 #include <sgidefs.h>
 #include <sysdeps/unix/sysdep.h>
 
+#define _SYS_AUXV_H 1
+#include <bits/hwcap.h>
+
 #ifdef __ASSEMBLER__
 
 #include <regdef.h>
diff --git a/sysdeps/unix/sysv/linux/mips/configure b/sysdeps/unix/sysv/linux/mips/configure
index e8b0d7b..db6c8ff 100644
--- a/sysdeps/unix/sysv/linux/mips/configure
+++ b/sysdeps/unix/sysv/linux/mips/configure
@@ -268,6 +268,33 @@ fi
 config_vars="$config_vars
 default-abi = ${libc_mips_abi}_${libc_mips_float}${libc_mips_nan}"
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for .module fp=xx support" >&5
+$as_echo_n "checking for .module fp=xx support... " >&6; }
+if ${libc_cv_asm_module_fpxx_directive+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat > conftest.s <<EOF
+.module fp=xx
+EOF
+if { ac_try='${CC-cc} -c $ASFLAGS conftest.s 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+  libc_cv_asm_module_fpxx_directive=yes
+else
+  libc_cv_asm_module_fpxx_directive=no
+fi
+rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_asm_module_fpxx_directive" >&5
+$as_echo "$libc_cv_asm_module_fpxx_directive" >&6; }
+if test $libc_cv_asm_module_fpxx_directive = yes; then
+  $as_echo "#define HAVE_MIPS_MODULE_FPXX_DIRECTIVE 1" >>confdefs.h
+
+fi
+
 case "$prefix" in
 /usr | /usr/)
   # 64-bit libraries on bi-arch platforms go in /lib64 instead of /lib.
diff --git a/sysdeps/unix/sysv/linux/mips/configure.ac b/sysdeps/unix/sysv/linux/mips/configure.ac
index 7087a14..cef2666 100644
--- a/sysdeps/unix/sysv/linux/mips/configure.ac
+++ b/sysdeps/unix/sysv/linux/mips/configure.ac
@@ -58,6 +58,21 @@ fi
 LIBC_CONFIG_VAR([default-abi],
   [${libc_mips_abi}_${libc_mips_float}${libc_mips_nan}])
 
+AC_CACHE_CHECK(for .module fp=xx support,
+               libc_cv_asm_module_fpxx_directive, [dnl
+cat > conftest.s <<EOF
+.module fp=xx
+EOF
+if AC_TRY_COMMAND(${CC-cc} -c $ASFLAGS conftest.s 1>&AS_MESSAGE_LOG_FD); then
+  libc_cv_asm_module_fpxx_directive=yes
+else
+  libc_cv_asm_module_fpxx_directive=no
+fi
+rm -f conftest*])
+if test $libc_cv_asm_module_fpxx_directive = yes; then
+  AC_DEFINE(HAVE_MIPS_MODULE_FPXX_DIRECTIVE)
+fi
+
 case "$prefix" in
 /usr | /usr/)
   # 64-bit libraries on bi-arch platforms go in /lib64 instead of /lib.
diff --git a/sysdeps/unix/sysv/linux/mips/getcontext.S b/sysdeps/unix/sysv/linux/mips/getcontext.S
index 1e0a277..14e7448 100644
--- a/sysdeps/unix/sysv/linux/mips/getcontext.S
+++ b/sysdeps/unix/sysv/linux/mips/getcontext.S
@@ -17,6 +17,10 @@
    License along with the GNU C Library.  If not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <config.h>
+#if HAVE_MIPS_MODULE_FPXX_DIRECTIVE
+	.module fp=xx
+#endif
 #include <sysdep.h>
 #include <sys/asm.h>
 #include <sys/fpregdef.h>
diff --git a/sysdeps/unix/sysv/linux/mips/setcontext.S b/sysdeps/unix/sysv/linux/mips/setcontext.S
index beeb2a5..217590c 100644
--- a/sysdeps/unix/sysv/linux/mips/setcontext.S
+++ b/sysdeps/unix/sysv/linux/mips/setcontext.S
@@ -17,6 +17,10 @@
    License along with the GNU C Library.  If not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <config.h>
+#if HAVE_MIPS_MODULE_FPXX_DIRECTIVE
+	.module fp=xx
+#endif
 #include <sysdep.h>
 #include <sys/asm.h>
 #include <sys/fpregdef.h>
diff --git a/sysdeps/unix/sysv/linux/mips/swapcontext.S b/sysdeps/unix/sysv/linux/mips/swapcontext.S
index 2a79976..1989e57 100644
--- a/sysdeps/unix/sysv/linux/mips/swapcontext.S
+++ b/sysdeps/unix/sysv/linux/mips/swapcontext.S
@@ -17,6 +17,10 @@
    License along with the GNU C Library.  If not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <config.h>
+#if HAVE_MIPS_MODULE_FPXX_DIRECTIVE
+	.module fp=xx
+#endif
 #include <sysdep.h>
 #include <sys/asm.h>
 #include <sys/fpregdef.h>
-- 
1.7.1

Attachment: hello.c
Description: hello.c

Attachment: libfp.c
Description: libfp.c

Attachment: Makefile
Description: Makefile


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