[RFC,MIPS] Add support for O32 FPXX and program header based ABI information
diff mbox

Message ID 6D39441BF12EF246A7ABCE6654B0235351E3D7@LEMAIL01.le.imgtec.org
State New
Headers show

Commit Message

Matthew Fortune May 1, 2014, 9:48 p.m. UTC
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

Comments

Joseph Myers May 1, 2014, 10:07 p.m. UTC | #1
We don't want to introduce new internal interfaces using #ifdef, because 
of the risk of typos being undetected.  Thus, ELF_MACHINE_NEEDS_PHDR_CHECK 
should be a macro that all architectures define to either 0 or 1, tested 
with #if not #ifdef.  Or, even better, have most architectures define a 
dummy version of elf_machine_phdr_check to which the call just gets 
optimized out, so no conditional code is needed in dl-load.c at all.

We don't want to introduce new uses of __builtin_expect; use 
__glibc_likely / __glibc_unlikely instead.

To avoid -Wundef warnings, and in line with the principle about avoiding 
#ifdef interfaces, make HAVE_MIPS_MODULE_FPXX_DIRECTIVE a 1/0 macro rather 
than a defined/undefined one.  (Right now you have it defined/undefined 
but also test with #if, leading to warnings in the undefined case.)
Matthew Fortune May 2, 2014, 6:55 a.m. UTC | #2
> We don't want to introduce new internal interfaces using #ifdef, because
> of the risk of typos being undetected.  Thus, ELF_MACHINE_NEEDS_PHDR_CHECK
> should be a macro that all architectures define to either 0 or 1, tested
> with #if not #ifdef.  Or, even better, have most architectures define a
> dummy version of elf_machine_phdr_check to which the call just gets
> optimized out, so no conditional code is needed in dl-load.c at all.

The dummy implementation is probably cleaner and exposes the hook clearly
to all the other architectures. I'll hold off making this change until
someone can approve the interface so I don't have to change it everywhere.
 
> We don't want to introduce new uses of __builtin_expect; use
> __glibc_likely / __glibc_unlikely instead.

OK

> To avoid -Wundef warnings, and in line with the principle about avoiding
> #ifdef interfaces, make HAVE_MIPS_MODULE_FPXX_DIRECTIVE a 1/0 macro rather
> than a defined/undefined one.  (Right now you have it defined/undefined
> but also test with #if, leading to warnings in the undefined case.)

My autoconf knowledge isn't brilliant. Should I do the following in the
not-supported case:

AC_DEFINE_UNQUOTED(HAVE_MIPS_MODULE_FPXX_DIRECTIVE, 0)

Or should/can I put the default in config.h.in like this:

#define HAVE_MIPS_MODULE_FPXX_DIRECTIVE 0

It looks like this will be the first config.h.in macro to be defined like
this so I'm not sure which way to go.

Regards,
Matthew
Will Newton May 2, 2014, 8:11 a.m. UTC | #3
On 1 May 2014 22:48, Matthew Fortune <Matthew.Fortune@imgtec.com> wrote:

Hi Matthew,

> 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));

I would be inclined to make these inline asm statements into a
function with a descriptive name, and potentially the status values
could usefully be named too.

> +         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");

Under what circumstances can this error occur?

> +       }
> +      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
>
Andrew Pinski May 2, 2014, 8:18 a.m. UTC | #4
On Fri, May 2, 2014 at 1:11 AM, Will Newton <will.newton@linaro.org> wrote:
> On 1 May 2014 22:48, Matthew Fortune <Matthew.Fortune@imgtec.com> wrote:
>
> Hi Matthew,
>
>> 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));
>
> I would be inclined to make these inline asm statements into a
> function with a descriptive name, and potentially the status values
> could usefully be named too.
>
>> +         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");
>
> Under what circumstances can this error occur?

The hardware does not support the requested mode.

Thanks,
Andrew Pinski

>
>> +       }
>> +      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
>>
>
>
>
> --
> Will Newton
> Toolchain Working Group, Linaro
Will Newton May 2, 2014, 8:25 a.m. UTC | #5
On 2 May 2014 09:18, Andrew Pinski <pinskia@gmail.com> wrote:
> On Fri, May 2, 2014 at 1:11 AM, Will Newton <will.newton@linaro.org> wrote:
>> On 1 May 2014 22:48, Matthew Fortune <Matthew.Fortune@imgtec.com> wrote:
>>
>> Hi Matthew,
>>
>>> 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));
>>
>> I would be inclined to make these inline asm statements into a
>> function with a descriptive name, and potentially the status values
>> could usefully be named too.
>>
>>> +         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");
>>
>> Under what circumstances can this error occur?
>
> The hardware does not support the requested mode.

Maybe "Hardware does not support mode %s" would be a more helpful
message in this case.
Matthew Fortune May 2, 2014, 9:09 a.m. UTC | #6
Will Newton <will.newton@linaro.org> writes:
> On 2 May 2014 09:18, Andrew Pinski <pinskia@gmail.com> wrote:

> > On Fri, May 2, 2014 at 1:11 AM, Will Newton <will.newton@linaro.org>

> wrote:

> >> On 1 May 2014 22:48, Matthew Fortune <Matthew.Fortune@imgtec.com>

> wrote:

> >>

... snip
> Val_GNU_MIPS_ABI_FP_DOUBLE)

> >>> +           switch_frmode_to (0);

> >>> +

> >>> +         asm volatile ("cfc1 %0,$1\n": "=r"(status));

> >>

> >> I would be inclined to make these inline asm statements into a

> >> function with a descriptive name, and potentially the status values

> >> could usefully be named too.


Good call. We tried to introduce more meaningful pseudo-instructions for this
feature but concluded that just using CTC1 was best.

> >>

> >>> +         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");

> >>

> >> Under what circumstances can this error occur?

> >

> > The hardware does not support the requested mode.


Yes-ish. Theoretically the kernel should not be advertising the UFR HWCAP
if one of the modes is not supported. UFR is only supported in hardware if
both modes exist but may well be implemented in trap-and-emulate on
MIPS32r2 and MIPS64 cores. If UFR is not supported nor emulated then the
CTC1 will lead to a crash. If the hardware and/or emulation doesn't work
correctly then this is a safety check to try and catch such failures. The
worst thing that could happen with this feature is a mode switch silently
failing; tracking down the bug would be horrendous.

> Maybe "Hardware does not support mode %s" would be a more helpful

> message in this case.


Since at this point of the code the hardware 'should' have been able to
enter the mode... I guess "Hardware failed to set mode %s".

regards,
Matthew
Matthew Fortune May 7, 2014, 7:29 p.m. UTC | #7
The GCC patch for this work has now been posted:

http://gcc.gnu.org/ml/gcc-patches/2014-05/msg00401.html

Should I address the current set of comments and re-post this patch
or should I wait for any further review?

Regards,
Matthew

> -----Original Message-----

> From: libc-alpha-owner@sourceware.org [mailto:libc-alpha-owner@sourceware.org] On Behalf

> Of Matthew Fortune

> Sent: 02 May 2014 10:09

> To: Will Newton; Andrew Pinski

> Cc: Joseph Myers (joseph@codesourcery.com); Richard Sandiford; Rich Fuhler;

> macro@codesourcery.com; libc-alpha@sourceware.org

> Subject: RE: [RFC, PATCH, MIPS] Add support for O32 FPXX and program header based ABI

> information

> 

> Will Newton <will.newton@linaro.org> writes:

> > On 2 May 2014 09:18, Andrew Pinski <pinskia@gmail.com> wrote:

> > > On Fri, May 2, 2014 at 1:11 AM, Will Newton <will.newton@linaro.org>

> > wrote:

> > >> On 1 May 2014 22:48, Matthew Fortune <Matthew.Fortune@imgtec.com>

> > wrote:

> > >>

> ... snip

> > Val_GNU_MIPS_ABI_FP_DOUBLE)

> > >>> +           switch_frmode_to (0);

> > >>> +

> > >>> +         asm volatile ("cfc1 %0,$1\n": "=r"(status));

> > >>

> > >> I would be inclined to make these inline asm statements into a

> > >> function with a descriptive name, and potentially the status values

> > >> could usefully be named too.

> 

> Good call. We tried to introduce more meaningful pseudo-instructions for this

> feature but concluded that just using CTC1 was best.

> 

> > >>

> > >>> +         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");

> > >>

> > >> Under what circumstances can this error occur?

> > >

> > > The hardware does not support the requested mode.

> 

> Yes-ish. Theoretically the kernel should not be advertising the UFR HWCAP

> if one of the modes is not supported. UFR is only supported in hardware if

> both modes exist but may well be implemented in trap-and-emulate on

> MIPS32r2 and MIPS64 cores. If UFR is not supported nor emulated then the

> CTC1 will lead to a crash. If the hardware and/or emulation doesn't work

> correctly then this is a safety check to try and catch such failures. The

> worst thing that could happen with this feature is a mode switch silently

> failing; tracking down the bug would be horrendous.

> 

> > Maybe "Hardware does not support mode %s" would be a more helpful

> > message in this case.

> 

> Since at this point of the code the hardware 'should' have been able to

> enter the mode... I guess "Hardware failed to set mode %s".

> 

> regards,

> Matthew
Joseph Myers May 7, 2014, 8:10 p.m. UTC | #8
On Wed, 7 May 2014, Matthew Fortune wrote:

> The GCC patch for this work has now been posted:
> 
> http://gcc.gnu.org/ml/gcc-patches/2014-05/msg00401.html
> 
> Should I address the current set of comments and re-post this patch
> or should I wait for any further review?

You should address the current comments and repost.

Patch
diff mbox

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>