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

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

Commit Message

Matthew Fortune May 14, 2014, 2:46 p.m. UTC
Hi Joseph,

I've updated the patch based on the comments.

I couldn't change the error message about failing to set the FR mode
as suggested because dl_signal_error does not support formatters. I did
however add debug diagnostics to state what mode change was being
attempted and this can be used in conjunction with the error to find out
what was happening. The diagnostic will also be useful simply to know
when a module triggers a successful mode change too.

The AFL* flags have been updated in line with changes to binutils FPXX
support.

The HAVE_MIPS_MODULE_FPXX_DIRECTIVE macro/configure test is no longer
needed owing to a change in how the assembler handles inferring an FP
ABI.

I have not yet tested all the FP ABI combinations but have covered the
ones which are related to FPXX. I am working through the rest as well
as n32/n64 ABIs.

I'm not sure what I should do for testing/checking the changes to all
the dl-machine.h files... I built an aarch64 version of glibc to check
at least one still builds and the same code is placed in all dl-machine.h
files. Each file appears to have a different whitespace policy so I left
either one or two newlines between functions depending on the file.

I would also like to add in another feature to check for the presence
of MSA in an object and reject it if HWCAP_MIPS_MSA is not set.  With
that in place users can construct MSA and non-MSA optimised libraries
and place the MSA library first in the search path and get the best
supported by the host.  This is possible because the MSA extension
makes no changes to the calling convention. Does that sound OK?

Regards,
Matthew

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

	* elf/dl-load.c (open_verify): Add 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): Likewise.
	(AFL_ASE_MSA, AFL_ASE_MSA64, AFL_ASE_MIPS16): Likewise.
	(AFL_ASE_MICROMIPS, AFL_ASE_XPA): 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.
	(find_mips_abiflags): New static inline function.
	(switch_frmode_to): New static no-inline function.
	(mips_fp_abi_string, 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/aarch64/dl-machine.h
	(elf_machine_phdr_check): New static function.
	* sysdeps/alpha/dl-machine.h: Likewise.
	* sysdeps/arm/dl-machine.h: Likewise.
	* sysdeps/generic/dl-machine.h: Likewise.
	* sysdeps/hppa/dl-machine.h: Likewise.
	* sysdeps/i386/dl-machine.h: Likewise.
	* sysdeps/ia64/dl-machine.h: Likewise.
	* sysdeps/m68k/dl-machine.h: Likewise.
	* sysdeps/microblaze/dl-machine.h: Likewise.
	* sysdeps/powerpc/powerpc32/dl-machine.h: Likewise.
	* sysdeps/powerpc/powerpc64/dl-machine.h: Likewise.
	* sysdeps/s390/s390-32/dl-machine.h: Likewise.
	* sysdeps/s390/s390-64/dl-machine.h: Likewise.
	* sysdeps/sh/dl-machine.h: Likewise.
	* sysdeps/sparc/sparc32/dl-machine.h: Likewise.
	* sysdeps/sparc/sparc64/dl-machine.h: Likewise.
	* sysdeps/tile/dl-machine.h: Likewise.
	* sysdeps/x86_64/dl-machine.h: Likewise.
---
 elf/dl-load.c                          |    5 +
 elf/elf.h                              |   94 ++++++++++++-
 sysdeps/aarch64/dl-machine.h           |    9 ++
 sysdeps/alpha/dl-machine.h             |    9 ++
 sysdeps/arm/dl-machine.h               |   10 ++
 sysdeps/generic/dl-machine.h           |    8 +
 sysdeps/hppa/dl-machine.h              |    9 ++
 sysdeps/i386/dl-machine.h              |   10 ++
 sysdeps/ia64/dl-machine.h              |    8 +
 sysdeps/m68k/dl-machine.h              |   10 ++
 sysdeps/microblaze/dl-machine.h        |    9 ++
 sysdeps/mips/bits/hwcap.h              |   23 +++
 sysdeps/mips/bits/linkmap.h            |    1 +
 sysdeps/mips/dl-machine.h              |  242 ++++++++++++++++++++++++++++++++
 sysdeps/mips/dl-procinfo.c             |   16 ++
 sysdeps/mips/dl-procinfo.h             |   50 ++++++--
 sysdeps/powerpc/powerpc32/dl-machine.h |    9 ++
 sysdeps/powerpc/powerpc64/dl-machine.h |    8 +
 sysdeps/s390/s390-32/dl-machine.h      |   10 ++
 sysdeps/s390/s390-64/dl-machine.h      |    9 ++
 sysdeps/sh/dl-machine.h                |   10 ++
 sysdeps/sparc/sparc32/dl-machine.h     |    9 ++
 sysdeps/sparc/sparc64/dl-machine.h     |    9 ++
 sysdeps/tile/dl-machine.h              |   10 ++
 sysdeps/unix/mips/sysdep.h             |    3 +
 sysdeps/x86_64/dl-machine.h            |    8 +
 27 files changed, 587 insertions(+), 13 deletions(-)
 create mode 100644 sysdeps/mips/bits/hwcap.h

Comments

Will Newton May 14, 2014, 4:27 p.m. UTC | #1
On 14 May 2014 15:46, Matthew Fortune <Matthew.Fortune@imgtec.com> wrote:

Hi Matthew,

> I've updated the patch based on the comments.
>
> I couldn't change the error message about failing to set the FR mode
> as suggested because dl_signal_error does not support formatters. I did
> however add debug diagnostics to state what mode change was being
> attempted and this can be used in conjunction with the error to find out
> what was happening. The diagnostic will also be useful simply to know
> when a module triggers a successful mode change too.
>
> The AFL* flags have been updated in line with changes to binutils FPXX
> support.
>
> The HAVE_MIPS_MODULE_FPXX_DIRECTIVE macro/configure test is no longer
> needed owing to a change in how the assembler handles inferring an FP
> ABI.
>
> I have not yet tested all the FP ABI combinations but have covered the
> ones which are related to FPXX. I am working through the rest as well
> as n32/n64 ABIs.
>
> I'm not sure what I should do for testing/checking the changes to all
> the dl-machine.h files... I built an aarch64 version of glibc to check
> at least one still builds and the same code is placed in all dl-machine.h
> files. Each file appears to have a different whitespace policy so I left
> either one or two newlines between functions depending on the file.
>
> I would also like to add in another feature to check for the presence
> of MSA in an object and reject it if HWCAP_MIPS_MSA is not set.  With
> that in place users can construct MSA and non-MSA optimised libraries
> and place the MSA library first in the search path and get the best
> supported by the host.  This is possible because the MSA extension
> makes no changes to the calling convention. Does that sound OK?
>
> Regards,
> Matthew
>
> 2014-05-14  Matthew Fortune  <matthew.fortune@imgtec.com>
>
>         * elf/dl-load.c (open_verify): Add 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): Likewise.
>         (AFL_ASE_MSA, AFL_ASE_MSA64, AFL_ASE_MIPS16): Likewise.
>         (AFL_ASE_MICROMIPS, AFL_ASE_XPA): 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.
>         (find_mips_abiflags): New static inline function.
>         (switch_frmode_to): New static no-inline function.
>         (mips_fp_abi_string, 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/aarch64/dl-machine.h
>         (elf_machine_phdr_check): New static function.
>         * sysdeps/alpha/dl-machine.h: Likewise.
>         * sysdeps/arm/dl-machine.h: Likewise.
>         * sysdeps/generic/dl-machine.h: Likewise.
>         * sysdeps/hppa/dl-machine.h: Likewise.
>         * sysdeps/i386/dl-machine.h: Likewise.
>         * sysdeps/ia64/dl-machine.h: Likewise.
>         * sysdeps/m68k/dl-machine.h: Likewise.
>         * sysdeps/microblaze/dl-machine.h: Likewise.
>         * sysdeps/powerpc/powerpc32/dl-machine.h: Likewise.
>         * sysdeps/powerpc/powerpc64/dl-machine.h: Likewise.
>         * sysdeps/s390/s390-32/dl-machine.h: Likewise.
>         * sysdeps/s390/s390-64/dl-machine.h: Likewise.
>         * sysdeps/sh/dl-machine.h: Likewise.
>         * sysdeps/sparc/sparc32/dl-machine.h: Likewise.
>         * sysdeps/sparc/sparc64/dl-machine.h: Likewise.
>         * sysdeps/tile/dl-machine.h: Likewise.
>         * sysdeps/x86_64/dl-machine.h: Likewise.
> ---
>  elf/dl-load.c                          |    5 +
>  elf/elf.h                              |   94 ++++++++++++-
>  sysdeps/aarch64/dl-machine.h           |    9 ++
>  sysdeps/alpha/dl-machine.h             |    9 ++
>  sysdeps/arm/dl-machine.h               |   10 ++
>  sysdeps/generic/dl-machine.h           |    8 +
>  sysdeps/hppa/dl-machine.h              |    9 ++
>  sysdeps/i386/dl-machine.h              |   10 ++
>  sysdeps/ia64/dl-machine.h              |    8 +
>  sysdeps/m68k/dl-machine.h              |   10 ++
>  sysdeps/microblaze/dl-machine.h        |    9 ++
>  sysdeps/mips/bits/hwcap.h              |   23 +++
>  sysdeps/mips/bits/linkmap.h            |    1 +
>  sysdeps/mips/dl-machine.h              |  242 ++++++++++++++++++++++++++++++++
>  sysdeps/mips/dl-procinfo.c             |   16 ++
>  sysdeps/mips/dl-procinfo.h             |   50 ++++++--
>  sysdeps/powerpc/powerpc32/dl-machine.h |    9 ++
>  sysdeps/powerpc/powerpc64/dl-machine.h |    8 +
>  sysdeps/s390/s390-32/dl-machine.h      |   10 ++
>  sysdeps/s390/s390-64/dl-machine.h      |    9 ++
>  sysdeps/sh/dl-machine.h                |   10 ++
>  sysdeps/sparc/sparc32/dl-machine.h     |    9 ++
>  sysdeps/sparc/sparc64/dl-machine.h     |    9 ++
>  sysdeps/tile/dl-machine.h              |   10 ++
>  sysdeps/unix/mips/sysdep.h             |    3 +
>  sysdeps/x86_64/dl-machine.h            |    8 +
>  27 files changed, 587 insertions(+), 13 deletions(-)
>  create mode 100644 sysdeps/mips/bits/hwcap.h
>
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index cfa7f25..201bf18 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -1697,6 +1697,11 @@ open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
>             }
>         }
>
> +      if (!__glibc_likely (
> +            elf_machine_phdr_check (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
> +                                    fd, loader)))
> +       goto close_and_out;
> +
>        /* 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..8d83b47 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,93 @@ 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_DSPR2          0x00000002 /* DSP R2 ASE.  */
> +#define AFL_ASE_EVA            0x00000004 /* Enhanced VA Scheme.  */
> +#define AFL_ASE_MCU            0x00000008 /* MCU (MicroController) ASE.  */
> +#define AFL_ASE_MDMX           0x00000010 /* MDMX ASE.  */
> +#define AFL_ASE_MIPS3D         0x00000020 /* MIPS-3D ASE.  */
> +#define AFL_ASE_MT             0x00000040 /* MT ASE.  */
> +#define AFL_ASE_SMARTMIPS      0x00000080 /* SmartMIPS ASE.  */
> +#define AFL_ASE_VIRT           0x00000100 /* VZ ASE.  */
> +#define AFL_ASE_MSA            0x00000200 /* MSA ASE.  */
> +#define AFL_ASE_MIPS16         0x00000400 /* MIPS16 ASE.  */
> +#define AFL_ASE_MICROMIPS      0x00000800 /* MICROMIPS ASE.  */
> +#define AFL_ASE_XPA            0x00001000 /* XPA ASE.  */
> +
> +/* Values for the isa_ext word of an ABI flags structure.  */
> +
> +#define AFL_EXT_XLR            1   /* RMI Xlr instruction.  */
> +#define AFL_EXT_OCTEON2                2   /* Cavium Networks Octeon2.  */
> +#define AFL_EXT_OCTEONP                3   /* Cavium Networks OcteonP.  */
> +#define AFL_EXT_LOONGSON_3A    4   /* Loongson 3A.  */
> +#define AFL_EXT_OCTEON         5   /* Cavium Networks Octeon.  */
> +#define AFL_EXT_5900           6   /* MIPS R5900 instruction.  */
> +#define AFL_EXT_4650           7   /* MIPS R4650 instruction.  */
> +#define AFL_EXT_4010           8   /* LSI R4010 instruction.  */
> +#define AFL_EXT_4100           9   /* NEC VR4100 instruction.  */
> +#define AFL_EXT_3900           10  /* Toshiba R3900 instruction.  */
> +#define AFL_EXT_10000          11  /* MIPS R10000 instruction.  */
> +#define AFL_EXT_SB1            12  /* Broadcom SB-1 instruction.  */
> +#define AFL_EXT_4111           13  /* NEC VR4111/VR4181 instruction.  */
> +#define AFL_EXT_4120           14  /* NEC VR4120 instruction.  */
> +#define AFL_EXT_5400           15  /* NEC VR5400 instruction.  */
> +#define AFL_EXT_5500           16  /* NEC VR5500 instruction.  */
> +#define AFL_EXT_LOONGSON_2E    17  /* ST Microelectronics Loongson 2E.  */
> +#define AFL_EXT_LOONGSON_2F    18  /* ST Microelectronics 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/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
> index 997c860..9770e3d 100644
> --- a/sysdeps/aarch64/dl-machine.h
> +++ b/sysdeps/aarch64/dl-machine.h
> @@ -32,6 +32,15 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
>    return ehdr->e_machine == EM_AARCH64;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}

I'm wondering whether the types could be improved here:

1. Should it return bool?
2. Should buf be const?
3. Should len be ssize_t?
4. The spacing around '*' is inconsistent.

> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT. */
>  static inline ElfW(Addr) __attribute__ ((unused))
> diff --git a/sysdeps/alpha/dl-machine.h b/sysdeps/alpha/dl-machine.h
> index 63db19c..9e65706 100644
> --- a/sysdeps/alpha/dl-machine.h
> +++ b/sysdeps/alpha/dl-machine.h
> @@ -42,6 +42,15 @@ elf_machine_matches_host (const Elf64_Ehdr *ehdr)
>    return ehdr->e_machine == EM_ALPHA;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* Return the link-time address of _DYNAMIC.  The multiple-got-capable
>     linker no longer allocates the first .got entry for this.  But not to
>     worry, no special tricks are needed.  */
> diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
> index 899b256..9bf4b89 100644
> --- a/sysdeps/arm/dl-machine.h
> +++ b/sysdeps/arm/dl-machine.h
> @@ -38,6 +38,16 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>  }
>
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  */
>  static inline Elf32_Addr __attribute__ ((unused))
> diff --git a/sysdeps/generic/dl-machine.h b/sysdeps/generic/dl-machine.h
> index d7a2b60..c036ec3 100644
> --- a/sysdeps/generic/dl-machine.h
> +++ b/sysdeps/generic/dl-machine.h
> @@ -33,6 +33,14 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>      }
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
>
>  /* Return the link-time address of _DYNAMIC.  */
>  static inline Elf32_Addr
> diff --git a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h
> index ba21f07..423d51c 100644
> --- a/sysdeps/hppa/dl-machine.h
> +++ b/sysdeps/hppa/dl-machine.h
> @@ -72,6 +72,15 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>    return ehdr->e_machine == EM_PARISC;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* Return the link-time address of _DYNAMIC.  */
>  static inline Elf32_Addr
>  elf_machine_dynamic (void) __attribute__ ((const));
> diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
> index 368bee2..6541c20 100644
> --- a/sysdeps/i386/dl-machine.h
> +++ b/sysdeps/i386/dl-machine.h
> @@ -34,6 +34,16 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>  }
>
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
> +
>  #ifdef PI_STATIC_AND_HIDDEN
>
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
> diff --git a/sysdeps/ia64/dl-machine.h b/sysdeps/ia64/dl-machine.h
> index 853e6fd..dfc6377 100644
> --- a/sysdeps/ia64/dl-machine.h
> +++ b/sysdeps/ia64/dl-machine.h
> @@ -54,6 +54,14 @@ elf_machine_matches_host (const Elf64_Ehdr *ehdr)
>    return ehdr->e_machine == EM_IA_64;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
>
>  /* Return the link-time address of _DYNAMIC.  */
>  static inline Elf64_Addr __attribute__ ((unused, const))
> diff --git a/sysdeps/m68k/dl-machine.h b/sysdeps/m68k/dl-machine.h
> index 3ec9862..68f62d7 100644
> --- a/sysdeps/m68k/dl-machine.h
> +++ b/sysdeps/m68k/dl-machine.h
> @@ -33,6 +33,16 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>  }
>
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
> +
>  /* Return the link-time address of _DYNAMIC.
>     This must be inlined in a function which uses global data.  */
>  static inline Elf32_Addr
> diff --git a/sysdeps/microblaze/dl-machine.h b/sysdeps/microblaze/dl-machine.h
> index 848e822..be7fe49 100644
> --- a/sysdeps/microblaze/dl-machine.h
> +++ b/sysdeps/microblaze/dl-machine.h
> @@ -31,6 +31,15 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>    return (ehdr->e_machine == EM_MICROBLAZE);
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  This must be inlined in a function which
>     uses global data.  */
> 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..86ca168 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,247 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
>      }
>  }
>
> +/* Search the program headers for the ABI Flags.  */
> +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));
> +}
> +
> +/* Obtain the current FR mode setting.  */
> +
> +static inline int
> +get_frmode (void)
> +{
> +  int frmode;
> +  asm volatile ("cfc1 %0,$1\n": "=r"(frmode));
> +  return frmode;
> +}
> +#endif
> +
> +/* Return a description of the specified floating-point ABI.  */
> +
> +static const char *
> +mips_fp_abi_string (int fpabi)
> +{
> +  switch (fpabi)
> +    {
> +    case Val_GNU_MIPS_ABI_FP_ANY:
> +      return "Hard or soft float";
> +    case Val_GNU_MIPS_ABI_FP_DOUBLE:
> +      return "Hard float (double precision)";
> +    case Val_GNU_MIPS_ABI_FP_SINGLE:
> +      return "Hard float (single precision)";
> +    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";
> +    }
> +}
> +
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  This verifies that floating-point ABIs are compatible and
> +   re-configures the hardware FR mode if necessary.  */
> +
> +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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +               _dl_debug_printf ("   Unable to read PT_MIPS_ABIFLAGS\n");
> +             return 0;
> +           }
> +       }
> +      if (size < sizeof (Elf_ABIFlags_v0))
> +       {
> +         if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +           _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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +                     _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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +             _dl_debug_printf ("   uses %s, already loaded %s\n",
> +                               mips_fp_abi_string (req_abi),
> +                               mips_fp_abi_string (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 frmode;
> +         /* Obtain current FR mode via UFR.  */
> +         frmode = get_frmode ();
> +         if (frmode == 0 && req_abi == Val_GNU_MIPS_ABI_FP_64)
> +           {
> +             if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +               _dl_debug_printf ("   setting FR mode to 1\n");
> +             switch_frmode_to (1);
> +           }
> +         else if (frmode == 1 && req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE)
> +           {
> +             if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +               _dl_debug_printf ("   setting FR mode to 0\n");
> +             switch_frmode_to (0);
> +           }
> +
> +         frmode = get_frmode ();
> +         if ((frmode == 0 && req_abi == Val_GNU_MIPS_ABI_FP_64)
> +             || (frmode == 1 && req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE))
> +           _dl_signal_error (0, map->l_name, NULL,
> +                             "hardware failed to set FR mode");
> +       }
> +      else if (req_abi == Val_GNU_MIPS_ABI_FP_64)
> +       {
> +         if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
> +           _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/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
> index 23b610f..2572eea 100644
> --- a/sysdeps/powerpc/powerpc32/dl-machine.h
> +++ b/sysdeps/powerpc/powerpc32/dl-machine.h
> @@ -36,6 +36,15 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>    return ehdr->e_machine == EM_PPC;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* Return the value of the GOT pointer.  */
>  static inline Elf32_Addr * __attribute__ ((const))
>  ppc_got (void)
> diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
> index bc99183..f0434b2 100644
> --- a/sysdeps/powerpc/powerpc64/dl-machine.h
> +++ b/sysdeps/powerpc/powerpc64/dl-machine.h
> @@ -80,6 +80,14 @@ elf_host_tolerates_class (const Elf64_Ehdr *ehdr)
>    return ehdr->e_ident[EI_CLASS] == ELFCLASS32;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
>
>  /* Return the run-time load address of the shared object, assuming it
>     was originally linked at zero.  */
> diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
> index 4fd2745..bbf7ba1 100644
> --- a/sysdeps/s390/s390-32/dl-machine.h
> +++ b/sysdeps/s390/s390-32/dl-machine.h
> @@ -46,6 +46,16 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>  }
>
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  This must be inlined in a function which
>     uses global data.  */
> diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
> index 2f37169..e480261 100644
> --- a/sysdeps/s390/s390-64/dl-machine.h
> +++ b/sysdeps/s390/s390-64/dl-machine.h
> @@ -41,6 +41,15 @@ elf_machine_matches_host (const Elf64_Ehdr *ehdr)
>          && ehdr->e_ident[EI_CLASS] == ELFCLASS64;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  This must be inlined in a function which
>     uses global data.  */
> diff --git a/sysdeps/sh/dl-machine.h b/sysdeps/sh/dl-machine.h
> index 4f3db89..ba2223e 100644
> --- a/sysdeps/sh/dl-machine.h
> +++ b/sysdeps/sh/dl-machine.h
> @@ -33,6 +33,16 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>  }
>
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  This must be inlined in a function which
>     uses global data.  */
> diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
> index e7d31b4..65a849f 100644
> --- a/sysdeps/sparc/sparc32/dl-machine.h
> +++ b/sysdeps/sparc/sparc32/dl-machine.h
> @@ -48,6 +48,15 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
>      return 0;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* We have to do this because elf_machine_{dynamic,load_address} can be
>     invoked from functions that have no GOT references, and thus the compiler
>     has no obligation to load the PIC register.  */
> diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
> index ef4ad4c..05f2669 100644
> --- a/sysdeps/sparc/sparc64/dl-machine.h
> +++ b/sysdeps/sparc/sparc64/dl-machine.h
> @@ -37,6 +37,15 @@ elf_machine_matches_host (const Elf64_Ehdr *ehdr)
>    return ehdr->e_machine == EM_SPARCV9;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
>  /* We have to do this because elf_machine_{dynamic,load_address} can be
>     invoked from functions that have no GOT references, and thus the compiler
>     has no obligation to load the PIC register.  */
> diff --git a/sysdeps/tile/dl-machine.h b/sysdeps/tile/dl-machine.h
> index d686a65..8fa86d2 100644
> --- a/sysdeps/tile/dl-machine.h
> +++ b/sysdeps/tile/dl-machine.h
> @@ -53,6 +53,16 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
>  }
>
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
> +
> +
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  This must be inlined in a function which
>     uses global data.  */
> 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/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
> index 8df04a9..290c405 100644
> --- a/sysdeps/x86_64/dl-machine.h
> +++ b/sysdeps/x86_64/dl-machine.h
> @@ -34,6 +34,14 @@ elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
>    return ehdr->e_machine == EM_X86_64;
>  }
>
> +/* Return nonzero iff ELF program headers are compatible with the running
> +   host.  */
> +static inline int
> +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
> +                       char * buf, int len, int fd, struct link_map * map)
> +{
> +  return 1;
> +}
>
>  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
>     first element of the GOT.  This must be inlined in a function which
> --
> 1.7.1
>
Joseph Myers May 14, 2014, 4:46 p.m. UTC | #2
On Wed, 14 May 2014, Matthew Fortune wrote:

> I have not yet tested all the FP ABI combinations but have covered the
> ones which are related to FPXX. I am working through the rest as well
> as n32/n64 ABIs.

I'd like to understand how all the various combinations of ABIs of objects 
and old and new compilers, binutils and libc (and kernel?) work.  Could 
you add information to the wiki page 
<https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking> 
about this?

That is:

* glibc might predate the new feature (and so implicitly require FR=0); it 
might postdate the feature, in which case it might be built with any 
combination of old or new GCC and old or new binutils.  If built with old 
GCC, it must be presumed to require FR=0; I don't know about the (new GCC, 
old binutils) combination.  (This is 5 cases for glibc.)

* The executable, and non-glibc shared libraries, might also be built with 
any combination of old or new GCC and old or new binutils - and one 
executable or shared library might contain a mixture of objects built with 
different tools.  (This is 4 cases for the tools building each .o file.  
But at least the new GCC, new binutils case divides into FR=0, FR=1 and 
interworking, so at least 6 cases.  Then the executable could have an 
arbitrary nonempty subset of those 6 cases - some of course would give 
link-time errors - and likewise a shared library.)  Then there's the 
question of what tools linked the executable or shared library, separate 
to what built the objects going into it - but one might say that the 
link-time tools must be at least as recent as the compile-time ones, so 
this doesn't add many more cases.

* However, a .o file requiring FR=1 may be presumed to be built with at 
least new GCC, given that the old definition of -mfp64 is being abandoned.

And I think the following requirements apply:

* If any object requires FR=1, either it must get FR=1 or there must be an 
error at static or dynamic link time.

* Likewise, FR=0.

* If all the objects' requirements are compatible, there must not be 
errors except in the case where a new object is passed to an old static or 
dynamic linker that gives an error because it doesn't understand or can't 
handle a new feature used in the new object.

* It should be possible to use new GCC and binutils to build objects / 
executables / shared libraries (not requiring FR=1) that work with old 
glibc.  This does not mean new FR=0 .o files need to be linkable with 
older binutils than the binutils that produced them, just that the final 
linked executables and shared libraries should be compatible with older 
glibc if that's the C library linked against at static link time and there 
are no FR=1 requirements.

So what is the logic that ensures that executables or shared libraries 
containing a .o file requiring FR=1 cannot be loaded by an old dynamic 
linker?  What about by a new dynamic linker built with an older GCC (as 
glibc will then require FR=0, though without an explicit markings to that 
effect)?

There are lots of different cases for combinations of objects - the wiki 
page needs to explain the reasoning that all of those cases are properly 
covered.  I'd guess it should discuss what combinations of GCC and 
binutils will allow objects requiring FR=1, or objects allowing 
interlinking, at all, and how (old objects, new objects requiring FR=0, 
new objects requiring FR=1, new objects allowing interlinking) are (a) 
distinguished as .o files, (b) linked, (c) distinguished as executables 
and shared libraries - and then go on to how the requirements are 
determined by the dynamic linker in a way that allows for old executables 
and shared libraries, and what it is about new executables and shared 
libraries that means old dynamic linkers won't handle them.  Some 
information is there, but it doesn't really seem to deal with the case of 
mixed objects built with different tools.

Then, how have these cases been tested?  It's probably not possible to 
integrate tests that require at least two different toolchains to build 
into the glibc testsuite, but I'd like to see the testsuite for these 
combinations posted.  Without a proper automated testsuite that covers 
mixing of old and new objects - as well as things such as verifying setjmp 
etc. work properly in the presence of mode changes - it's very hard to be 
confident in the patch.

From what I've listed you have at least 5 cases for glibc times 2^6-1 for 
the executable times 2^6-1 for a shared library it uses - even if actually 
it's more than 2^6-1 the numbers are small enough (unlikely to be more 
than a million tests - I've generated larger sets of tests than that 
before when verifying ABI compatibility issues) for exhausive testing that 
the combinations give errors exactly when the should to be feasible.  And 
practically, the numbers could be reduced a lot by splitting things into 
(a) verifying that each of the 2^6-1 combinations of .o files produces the 
right ELF headers in the linked .so or executable, or is rejected when 
appropriate, (b) just checking the different cases for those headers in 
runtime tests.

(It's possible the old-dynamic-linker case can be handled by setting the 
ABI version, depending on how far the bitrot discussed in 
<https://sourceware.org/ml/libc-alpha/2014-01/msg00375.html> (which I 
referred to in <https://sourceware.org/ml/binutils/2014-04/msg00237.html>) 
extends.  Or if that won't work, making FR=1 objects contain a reference 
to a new symbol glibc exports at version GLIBC_2.20 would work.)

(I'm a bit less concerned about ensuring new .o files are rejected by the 
old static linker - anyway, that's not a glibc issue - although it's 
certainly good if they are, at least if they require FR=1, rather than 
being quietly linked to an executable or shared library that appears to 
require FR=0 when actually it requires FR=1 or has internally 
contradictory requirements.)

> I would also like to add in another feature to check for the presence
> of MSA in an object and reject it if HWCAP_MIPS_MSA is not set.  With
> that in place users can construct MSA and non-MSA optimised libraries
> and place the MSA library first in the search path and get the best
> supported by the host.  This is possible because the MSA extension
> makes no changes to the calling convention. Does that sound OK?

What do you mean by "presence of MSA in an object"?

It's normal and OK for code to do things like

  if (msa_present)
    func_msa ();
  else
    func_non_msa ();

where the two functions are in different source files, built with 
different options.  Or to do the equivalent with IFUNCs.  Or to use the 
"target" GCC attribute to have the functions built with different options 
in the same .o file.  So the presence of MSA instructions in an object 
file can't be taken to indicate user intent that the final linked 
executable or shared library requires MSA.  Do you have any existing 
examples of such runtime rejection on other architectures?

The correct way to handle MSA and non-MSA libraries is to include 
HWCAP_MIPS_MSA in HWCAP_IMPORTANT so that the dynamic linker will 
automatically search appropriate subdirectories of shared library 
directories.

Another possible issue with this patch:

* I don't think any floating-point asms should be compiled in for the 
__mips_soft_float case (or equivalently, they should be conditioned on 
__mips_hard_float) - for soft-float, the assembler may reject hard-float 
instructions.  Most of the new code is irrelevant in that case (though it 
would be nice to reject hard-float libraries in soft-float ld.so, if the 
new ELF information makes that possible).

* Floating-point asms also won't work when glibc is built as MIPS16, so 
some files may need building -mno-mips16, or __attribute__ ((nomips16)) 
added to relevant functions, if it isn't already there.
Matthew Fortune May 14, 2014, 9:13 p.m. UTC | #3
Thanks for the feedback. Comments inline.

Joseph Myers <joseph@codesourcery.com> writes:
> On Wed, 14 May 2014, Matthew Fortune wrote:
> 
> > I have not yet tested all the FP ABI combinations but have covered the
> > ones which are related to FPXX. I am working through the rest as well
> > as n32/n64 ABIs.
> 
> I'd like to understand how all the various combinations of ABIs of objects
> and old and new compilers, binutils and libc (and kernel?) work.  Could
> you add information to the wiki page
> <https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking>
> about this?

Absolutely. I tried to keep the information on compatibility specifically
about the three (fp32, fpxx, fp64) ABIs as newly defined. This is
mainly so that the spec can be applied to any set of tools regardless of
specific implementation issues. I still ended up with some GCC/binutils/glibc
centric ideas in there but that was not intentional.

I'll do a separate page about the wider compatibility implications for
the pre-existing FSF tools.

> That is:
> 
> * glibc might predate the new feature (and so implicitly require FR=0); it
> might postdate the feature, in which case it might be built with any
> combination of old or new GCC and old or new binutils.  If built with old
> GCC, it must be presumed to require FR=0; I don't know about the (new GCC,
> old binutils) combination.  (This is 5 cases for glibc.)
> 
> * The executable, and non-glibc shared libraries, might also be built with
> any combination of old or new GCC and old or new binutils - and one
> executable or shared library might contain a mixture of objects built with
> different tools.  (This is 4 cases for the tools building each .o file.
> But at least the new GCC, new binutils case divides into FR=0, FR=1 and
> interworking, so at least 6 cases.  Then the executable could have an
> arbitrary nonempty subset of those 6 cases - some of course would give
> link-time errors - and likewise a shared library.)  Then there's the
> question of what tools linked the executable or shared library, separate
> to what built the objects going into it - but one might say that the
> link-time tools must be at least as recent as the compile-time ones, so
> this doesn't add many more cases.
> 
> * However, a .o file requiring FR=1 may be presumed to be built with at
> least new GCC, given that the old definition of -mfp64 is being abandoned.
> 
> And I think the following requirements apply:
> 
> * If any object requires FR=1, either it must get FR=1 or there must be an
> error at static or dynamic link time.
> 
> * Likewise, FR=0.
> 
> * If all the objects' requirements are compatible, there must not be
> errors except in the case where a new object is passed to an old static or
> dynamic linker that gives an error because it doesn't understand or can't
> handle a new feature used in the new object.
> 
> * It should be possible to use new GCC and binutils to build objects /
> executables / shared libraries (not requiring FR=1) that work with old
> glibc.  This does not mean new FR=0 .o files need to be linkable with
> older binutils than the binutils that produced them, just that the final
> linked executables and shared libraries should be compatible with older
> glibc if that's the C library linked against at static link time and there
> are no FR=1 requirements.
> 
> So what is the logic that ensures that executables or shared libraries
> containing a .o file requiring FR=1 cannot be loaded by an old dynamic
> linker?

I have not proposed any such logic. My opinion is that this is not a
critical thing to have but if we can find a sensible solution then
that is fine. Other aspects of this work improve the error checking that
I do not believe any existing dynamic linker handles i.e. checking that
soft and hard float shared libraries/executables are not linked. 

> What about by a new dynamic linker built with an older GCC (as
> glibc will then require FR=0, though without an explicit markings to that
> effect)?
> 
> There are lots of different cases for combinations of objects - the wiki
> page needs to explain the reasoning that all of those cases are properly
> covered.  I'd guess it should discuss what combinations of GCC and
> binutils will allow objects requiring FR=1, or objects allowing
> interlinking, at all, and how (old objects, new objects requiring FR=0,
> new objects requiring FR=1, new objects allowing interlinking) are (a)
> distinguished as .o files, (b) linked, (c) distinguished as executables
> and shared libraries - and then go on to how the requirements are
> determined by the dynamic linker in a way that allows for old executables
> and shared libraries, and what it is about new executables and shared
> libraries that means old dynamic linkers won't handle them.  Some
> information is there, but it doesn't really seem to deal with the case of
> mixed objects built with different tools.

I can document how this works. 
 
> Then, how have these cases been tested?  It's probably not possible to
> integrate tests that require at least two different toolchains to build
> into the glibc testsuite, but I'd like to see the testsuite for these
> combinations posted.  Without a proper automated testsuite that covers
> mixing of old and new objects - as well as things such as verifying setjmp
> etc. work properly in the presence of mode changes - it's very hard to be
> confident in the patch.

I'm up to the point of finishing static link tests for this and wanted
advice (such as what you have given) with respect to glibc testing. It is
possible to 'fake' objects from previous toolchains by stripping sections
prior to final link but it would be a very complex test process.

> From what I've listed you have at least 5 cases for glibc times 2^6-1 for
> the executable times 2^6-1 for a shared library it uses - even if actually
> it's more than 2^6-1 the numbers are small enough (unlikely to be more
> than a million tests - I've generated larger sets of tests than that
> before when verifying ABI compatibility issues) for exhausive testing that
> the combinations give errors exactly when the should to be feasible.  And
> practically, the numbers could be reduced a lot by splitting things into
> (a) verifying that each of the 2^6-1 combinations of .o files produces the
> right ELF headers in the linked .so or executable, or is rejected when
> appropriate, (b) just checking the different cases for those headers in
> runtime tests.

I will be/am separating the scope of the testing and for glibc I intend to
simply categorize/enumerate the possible inputs in terms of finally linked
executables and shared libraries. This will be in terms of e_flags and
.MIPS.abiflags content (or lack thereof). The tests for the ways in which
you can generate said executables will be part of binutils where the e_flags
and .MIPS.abiflags will be verified.

> (It's possible the old-dynamic-linker case can be handled by setting the
> ABI version, depending on how far the bitrot discussed in
> <https://sourceware.org/ml/libc-alpha/2014-01/msg00375.html> (which I
> referred to in <https://sourceware.org/ml/binutils/2014-04/msg00237.html>)
> extends.  Or if that won't work, making FR=1 objects contain a reference
> to a new symbol glibc exports at version GLIBC_2.20 would work.)

I'd be happy with setting a new ABI version for FP64. Reading through the
code it looks like this will lead to existing dynamic linkers rejecting
the ELF so could work nicely. I'd be somewhat wary of having to do something
like reference a new symbol to get the desired behaviour. I'll make sure to
discuss this with Richard on the binutils thread if he is not following this
thread in detail.

> (I'm a bit less concerned about ensuring new .o files are rejected by the
> old static linker - anyway, that's not a glibc issue - although it's
> certainly good if they are, at least if they require FR=1, rather than
> being quietly linked to an executable or shared library that appears to
> require FR=0 when actually it requires FR=1 or has internally
> contradictory requirements.)

This is actually handled naturally as all the ABI work here builds on top
of existing gnu_attribute support and as such old linkers will warn about
new objects with unknown ABIs.

> > I would also like to add in another feature to check for the presence
> > of MSA in an object and reject it if HWCAP_MIPS_MSA is not set.  With
> > that in place users can construct MSA and non-MSA optimised libraries
> > and place the MSA library first in the search path and get the best
> > supported by the host.  This is possible because the MSA extension
> > makes no changes to the calling convention. Does that sound OK?
> 
> What do you mean by "presence of MSA in an object"?

That was very sloppy phrasing. I'll explain by answering your comments
below.

> It's normal and OK for code to do things like
> 
>   if (msa_present)
>     func_msa ();
>   else
>     func_non_msa ();
> 
> where the two functions are in different source files, built with
> different options.  Or to do the equivalent with IFUNCs.  Or to use the
> "target" GCC attribute to have the functions built with different options
> in the same .o file.  So the presence of MSA instructions in an object
> file can't be taken to indicate user intent that the final linked
> executable or shared library requires MSA.

Absolutely. Such an executable/shared library would not be marked as using
MSA (because it may not depending on runtime). However an executable that
is simply built with MSA enabled throughout will be marked as using the
MSA ASE unconditionally. It is the simple case of unconditionally using
MSA that I would like to improve error checking for.

> Do you have any existing
> examples of such runtime rejection on other architectures?

No, but lack of prior art does not prevent innovation.

> The correct way to handle MSA and non-MSA libraries is to include
> HWCAP_MIPS_MSA in HWCAP_IMPORTANT so that the dynamic linker will
> automatically search appropriate subdirectories of shared library
> directories.

I can't find any information on exactly how all the HWCAP_IMPORTANT logic
hangs together (except the source) but as far as I can see this is about
how to construct an implicit search tree to locate objects. It does
not give any assurance that an object found in any particular search
location is actually compatible with current hardware. I'm also wondering
as to how the average end user writing a shared library is expected to
install their libraries into the appropriate directories.

From what I see I agree this is the way to get the search tree
constructed for optional elements like MSA. I'd like to improve on that
as MIPS shared libraries will have enough information in them to
ensure that any given library is specifically supported by the current
hardware. (I also see that I should not have but UFR in HWCAP_IMPORTANT)

> Another possible issue with this patch:
> 
> * I don't think any floating-point asms should be compiled in for the
> __mips_soft_float case (or equivalently, they should be conditioned on
> __mips_hard_float) - for soft-float, the assembler may reject hard-float
> instructions.  Most of the new code is irrelevant in that case (though it
> would be nice to reject hard-float libraries in soft-float ld.so, if the
> new ELF information makes that possible).

Yes, that is a bug. The code is designed to handle the checks for
soft-float vs hard-float checks and so I will just remove the parts that
deal with mode switching from the soft-float build.

> * Floating-point asms also won't work when glibc is built as MIPS16, so
> some files may need building -mno-mips16, or __attribute__ ((nomips16))
> added to relevant functions, if it isn't already there.

Also missed that, thanks. 

I'll let you know when I have written up the details you asked for.

Regards,
Matthew
Joseph Myers May 14, 2014, 9:29 p.m. UTC | #4
On Wed, 14 May 2014, Matthew Fortune wrote:

> Thanks for the feedback. Comments inline.
> 
> Joseph Myers <joseph@codesourcery.com> writes:
> > On Wed, 14 May 2014, Matthew Fortune wrote:
> > 
> > > I have not yet tested all the FP ABI combinations but have covered the
> > > ones which are related to FPXX. I am working through the rest as well
> > > as n32/n64 ABIs.
> > 
> > I'd like to understand how all the various combinations of ABIs of objects
> > and old and new compilers, binutils and libc (and kernel?) work.  Could
> > you add information to the wiki page
> > <https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking>
> > about this?
> 
> Absolutely. I tried to keep the information on compatibility specifically
> about the three (fp32, fpxx, fp64) ABIs as newly defined. This is
> mainly so that the spec can be applied to any set of tools regardless of
> specific implementation issues. I still ended up with some GCC/binutils/glibc
> centric ideas in there but that was not intentional.

Indeed, that page seems to do rather better on how things work with 
all-new objects, than on what happens with old objects, or objects built 
with one of GCC and binutils new and the other old.

> I have not proposed any such logic. My opinion is that this is not a
> critical thing to have but if we can find a sensible solution then
> that is fine. Other aspects of this work improve the error checking that
> I do not believe any existing dynamic linker handles i.e. checking that
> soft and hard float shared libraries/executables are not linked. 

The mood now is to do better in future for such cases by expecting each 
new ABI (even for a new architecture) to have a unique dynamic linker 
name.  (Of course that doesn't help for existing MIPS ABIs where the 
dynamic linker names are already fixed.)

> > (It's possible the old-dynamic-linker case can be handled by setting the
> > ABI version, depending on how far the bitrot discussed in
> > <https://sourceware.org/ml/libc-alpha/2014-01/msg00375.html> (which I
> > referred to in <https://sourceware.org/ml/binutils/2014-04/msg00237.html>)
> > extends.  Or if that won't work, making FR=1 objects contain a reference
> > to a new symbol glibc exports at version GLIBC_2.20 would work.)
> 
> I'd be happy with setting a new ABI version for FP64. Reading through the
> code it looks like this will lead to existing dynamic linkers rejecting
> the ELF so could work nicely. I'd be somewhat wary of having to do something
> like reference a new symbol to get the desired behaviour. I'll make sure to
> discuss this with Richard on the binutils thread if he is not following this
> thread in detail.

I suspect some bitrot in the existing feature may well need fixing first 
(whether in binutils or glibc), but it's certainly the natural feature to 
use if possible, being intended to ensure that binaries using new ELF 
features are rejected by older dynamic linkers.

> > It's normal and OK for code to do things like
> > 
> >   if (msa_present)
> >     func_msa ();
> >   else
> >     func_non_msa ();
> > 
> > where the two functions are in different source files, built with
> > different options.  Or to do the equivalent with IFUNCs.  Or to use the
> > "target" GCC attribute to have the functions built with different options
> > in the same .o file.  So the presence of MSA instructions in an object
> > file can't be taken to indicate user intent that the final linked
> > executable or shared library requires MSA.
> 
> Absolutely. Such an executable/shared library would not be marked as using
> MSA (because it may not depending on runtime). However an executable that
> is simply built with MSA enabled throughout will be marked as using the
> MSA ASE unconditionally. It is the simple case of unconditionally using
> MSA that I would like to improve error checking for.

That suggests you need a new -mlink-require-msa or similar option to put 
this setting in an executable / shared library, given that existing 
practice is that use of an option such as -mmsa for some objects does not 
imply an MSA requirement for the result of linking - users don't expect to 
need any special options for code with that sort of runtime checking, they 
just build different objects with different options (and you're unlikely 
to get -mmsa used for all objects in any executable / shared library - if 
nothing else, crt*.o, libgcc etc. probably aren't built with -mmsa - so 
merging on the basis that MSA + non-MSA = non-MSA won't help).
Matthew Fortune May 15, 2014, 6:36 a.m. UTC | #5
Joseph Myers <joseph@codesourcery.com> writes:
> On Wed, 14 May 2014, Matthew Fortune wrote:
> > Joseph Myers <joseph@codesourcery.com> writes:
> > > It's normal and OK for code to do things like
> > >
> > >   if (msa_present)
> > >     func_msa ();
> > >   else
> > >     func_non_msa ();
> > >
> > > where the two functions are in different source files, built with
> > > different options.  Or to do the equivalent with IFUNCs.  Or to use the
> > > "target" GCC attribute to have the functions built with different
> options
> > > in the same .o file.  So the presence of MSA instructions in an object
> > > file can't be taken to indicate user intent that the final linked
> > > executable or shared library requires MSA.
> >
> > Absolutely. Such an executable/shared library would not be marked as
> using
> > MSA (because it may not depending on runtime). However an executable that
> > is simply built with MSA enabled throughout will be marked as using the
> > MSA ASE unconditionally. It is the simple case of unconditionally using
> > MSA that I would like to improve error checking for.
> 
> That suggests you need a new -mlink-require-msa or similar option to put
> this setting in an executable / shared library, given that existing
> practice is that use of an option such as -mmsa for some objects does not
> imply an MSA requirement for the result of linking - users don't expect to
> need any special options for code with that sort of runtime checking, they
> just build different objects with different options (and you're unlikely
> to get -mmsa used for all objects in any executable / shared library - if
> nothing else, crt*.o, libgcc etc. probably aren't built with -mmsa - so
> merging on the basis that MSA + non-MSA = non-MSA won't help).

Binutils is tuned for the simple case of users writing code that does not do
runtime checks. i.e. as foo.s -mmsa results in an object that says it uses
the MSA ASE. The merging of ASE usage into an executable or shared library
is an 'OR' so MSA + non-MSA == MSA. To create a shared library or executable
which includes runtime checks before executing any MSA code then the MSA
code has to be hidden:

as foo.s
===
.set push
.set msa
<msa function>
.set pop
===

The resulting object will then 'not' be marked as using the MSA ASE.

This gives us two classes of binary. The ones which include runtime
checks before using MSA are safe by design. The ones which the
average user will build are not 100% safe and can only rely on search
paths being set correctly and MSA-enabled libraries not being found in
a non-MSA search path.

The proposal is just to add a small amount of extra functionality which
serves two purposes:

1) If a program running on non-MSA hardware ends up linking with an
   MSA library then the user is informed via link error instead of
   sigill.
2) If (1) occurs and there are still search paths to look in then the
   dynamic linker may still resolve the dependency with a library that
   can be loaded on the current hardware.

This is a natural extension of the functionality added by FPXX as the
FPXX logic includes this kind of link time rejection in the hope that
a compatible object will be found later.

Thinking about something I wrote last night...
I said I was wrong to have included the HWCAP_MIPS_UFR feature in
HWCAP_IMPORTANT but actually it is probably the right thing to do
as it serves to define a search path (ufr) where FP64 libraries
can be placed.

Regards,
Matthew
Matthew Fortune May 15, 2014, 6:41 a.m. UTC | #6
> On 14 May 2014 15:46, Matthew Fortune <Matthew.Fortune@imgtec.com> wrote:

> 

> Hi Matthew,

> > +/* Return nonzero iff ELF program headers are compatible with the

> running

> > +   host.  */

> > +static inline int

> > +elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,

> > +                       char * buf, int len, int fd, struct link_map *

> map)

> > +{

> > +  return 1;

> > +}

> 

> I'm wondering whether the types could be improved here:

> 

> 1. Should it return bool?

> 2. Should buf be const?

> 3. Should len be ssize_t?

> 4. The spacing around '*' is inconsistent.


All good points. I knew I’d end up needing to change all the stubs again.
I think the answer is yes to all.  The types (except const'ness) were just
following the types of the data which are being passed in from dl-load and
also the convention used for return types of other hooks.

If I change the type of len then I expect I should change the type of the
original variable in dl-load as well.

Regards,
Matthew
Joseph Myers May 15, 2014, 3:20 p.m. UTC | #7
On Thu, 15 May 2014, Matthew Fortune wrote:

> > That suggests you need a new -mlink-require-msa or similar option to put
> > this setting in an executable / shared library, given that existing
> > practice is that use of an option such as -mmsa for some objects does not
> > imply an MSA requirement for the result of linking - users don't expect to
> > need any special options for code with that sort of runtime checking, they
> > just build different objects with different options (and you're unlikely
> > to get -mmsa used for all objects in any executable / shared library - if
> > nothing else, crt*.o, libgcc etc. probably aren't built with -mmsa - so
> > merging on the basis that MSA + non-MSA = non-MSA won't help).
> 
> Binutils is tuned for the simple case of users writing code that does not do
> runtime checks. i.e. as foo.s -mmsa results in an object that says it uses
> the MSA ASE. The merging of ASE usage into an executable or shared library
> is an 'OR' so MSA + non-MSA == MSA. To create a shared library or executable
> which includes runtime checks before executing any MSA code then the MSA
> code has to be hidden:
> 
> as foo.s
> ===
> .set push
> .set msa
> <msa function>
> .set pop
> ===
> 
> The resulting object will then 'not' be marked as using the MSA ASE.

That indicates to me that the GCC -mmsa option should not imply the 
assembler -mmsa option, so that users can use -mmsa with GCC exactly the 
same way they use -mavx (for example) - that is, -mmsa should result in 
the push/pop in the .s file.  Otherwise, how do you expect users compiling 
one source file with -mmsa and one without to get their final binary not 
marked as requiring MSA?
Matthew Fortune May 15, 2014, 7:56 p.m. UTC | #8
Joseph Myers <joseph@codesourcery.com>writes:
> On Thu, 15 May 2014, Matthew Fortune wrote:
> 
> > > That suggests you need a new -mlink-require-msa or similar option to
> put
> > > this setting in an executable / shared library, given that existing
> > > practice is that use of an option such as -mmsa for some objects does
> not
> > > imply an MSA requirement for the result of linking - users don't expect
> to
> > > need any special options for code with that sort of runtime checking,
> they
> > > just build different objects with different options (and you're
> unlikely
> > > to get -mmsa used for all objects in any executable / shared library -
> if
> > > nothing else, crt*.o, libgcc etc. probably aren't built with -mmsa - so
> > > merging on the basis that MSA + non-MSA = non-MSA won't help).
> >
> > Binutils is tuned for the simple case of users writing code that does not
> do
> > runtime checks. i.e. as foo.s -mmsa results in an object that says it
> uses
> > the MSA ASE. The merging of ASE usage into an executable or shared
> library
> > is an 'OR' so MSA + non-MSA == MSA. To create a shared library or
> executable
> > which includes runtime checks before executing any MSA code then the MSA
> > code has to be hidden:
> >
> > as foo.s
> > ===
> > .set push
> > .set msa
> > <msa function>
> > .set pop
> > ===
> >
> > The resulting object will then 'not' be marked as using the MSA ASE.
> 
> That indicates to me that the GCC -mmsa option should not imply the
> assembler -mmsa option, so that users can use -mmsa with GCC exactly the
> same way they use -mavx (for example) - that is, -mmsa should result in
> the push/pop in the .s file.  Otherwise, how do you expect users compiling
> one source file with -mmsa and one without to get their final binary not
> marked as requiring MSA?

I don't currently expect an object built with -mmsa and one without to result
in an executable that does not say it requires MSA. If someone wishes to hide
the usage of an extended architecture feature then that it must be enabled on
a per function basis so that it is clear that they are special. For MSA this
would be done via a function attribute and could be applied for all
functions in a compilation unit too. We have to differentiate between
assumed existence of MSA and smart runtime checks to choose the right function.
I would expect that if someone builds a simd optimised library by just adding
the relevant simd command line flag then this is to build a whole library of
simd not half simd and half generic so it is right and correct to mark that
library as using the simd extension. For those cases where there is some
small usage of simd like ifunc (or a more manual version of ifunc) then the
function annotation to enable MSA is not onerous.

Regarding comparing MSA with AVX...

I don't know for sure but I don't believe the use of AVX can be recorded in
an ELF (at least not in any way that the loader can see) so it is not
necessarily a conscious decision to not mark the ELF, it is arguably a
missing feature.

Related to this is the fact that AVX ABI decisions differ quite
significantly from MSA as MIPS has opted to not extend/change the calling
convention for vector types when targeting MSA. This is specifically to
support having generic code (that may include vector types passed by value)
to be compiled as either MSA or non-MSA and avoid the need for the caller
to know how the target function was compiled. While it is unlikely for
ordinary code to use vector types passed by value MIPS chose to ensure this
case did not cause ABI compatibility issues.

To what extent do you feel the use of an ISA extension must be controlled
via a command line option for the various use cases?

Regards,
Matthew
Joseph Myers May 15, 2014, 8:18 p.m. UTC | #9
On Thu, 15 May 2014, Matthew Fortune wrote:

> > That indicates to me that the GCC -mmsa option should not imply the
> > assembler -mmsa option, so that users can use -mmsa with GCC exactly the
> > same way they use -mavx (for example) - that is, -mmsa should result in
> > the push/pop in the .s file.  Otherwise, how do you expect users compiling
> > one source file with -mmsa and one without to get their final binary not
> > marked as requiring MSA?
> 
> I don't currently expect an object built with -mmsa and one without to result
> in an executable that does not say it requires MSA. If someone wishes to hide
> the usage of an extended architecture feature then that it must be enabled on
> a per function basis so that it is clear that they are special. For MSA this

I consider that a key feature of the GNU toolchain is consistency between 
different architectures - meaning that if this sort of thing works on 
pretty much all architectures, MIPS shouldn't be doing something 
different.  (And it *certainly* shouldn't be requiring things to be done 
on a per-function basis.  Even if the command-line option acts 
differently, there should be a command-line option to say that the object 
isn't marked as needing MSA - though I'd say it should be the other way 
round, with a -m option to say to mark the object for runtime 
compatibility checks.)

I suppose that, to the extent that LTO options merging may already cause 
issues when different objects are built with different options, there may 
be a case for a -f option meaning "this object uses instruction set 
extensions in functions that may or may not be used at runtime, do not 
automatically use those options for other objects in this link and do not 
count those extensions for runtime compatibility checks".
Matthew Fortune May 16, 2014, 2:17 p.m. UTC | #10
Joseph Myers <joseph@codesourcery.com> writes:
> On Thu, 15 May 2014, Matthew Fortune wrote:
> 
> > > That indicates to me that the GCC -mmsa option should not imply the
> > > assembler -mmsa option, so that users can use -mmsa with GCC exactly
> the
> > > same way they use -mavx (for example) - that is, -mmsa should result
> in
> > > the push/pop in the .s file.  Otherwise, how do you expect users
> compiling
> > > one source file with -mmsa and one without to get their final binary
> not
> > > marked as requiring MSA?
> >
> > I don't currently expect an object built with -mmsa and one without to
> result
> > in an executable that does not say it requires MSA. If someone wishes
> to hide
> > the usage of an extended architecture feature then that it must be
> enabled on
> > a per function basis so that it is clear that they are special. For
> MSA this
> 
> I consider that a key feature of the GNU toolchain is consistency
> between
> different architectures - meaning that if this sort of thing works on
> pretty much all architectures, MIPS shouldn't be doing something
> different.  (And it *certainly* shouldn't be requiring things to be done
> on a per-function basis.  Even if the command-line option acts
> differently, there should be a command-line option to say that the
> object
> isn't marked as needing MSA - though I'd say it should be the other way
> round, with a -m option to say to mark the object for runtime
> compatibility checks.)

I don't understand the use-case that would want a whole module (and/or
subset of modules within one library) to freely deploy an ISA extension
but have no overall marking to that extent. Such global enablement must be
taken to indicate unconditional usage of an extension. If there is some
real use-case where the result of linking such modules does not want to
be marked appropriately then I see that as the special case. If AVX has
no such feature then it needs improving rather than having all other
architectures gloss over the problem too.

My only assumption is that you envisage a library where pretty much
all entry points are ifunc (or similar) and there are 'n' implementations
of highly complex features that span across multiple source files. Assuming
this were found to be useful (vs the simple ifunc case of small features
being optimised) then it seems better to support it by having the library
built 'n' times with the various supported extensions and have the library
selected as a whole rather than per-function/feature. If nothing else this
will result in lower overall memory footprint as only the code that will be
used gets loaded. It is also a simpler model for those who wish to just
produce libraries of code optimised for different extensions.

I see evidence that ARM provide some support this model of whole library
optimisation by having VFP and NEON HWCAPs part of HWCAP_IMPORTANT. I don't
believe there is any verification that the VFP or NEON optimised libraries
are 'only' found in the relevant search paths but that could be improved.

Checking that the appropriate hardware is available for a library is very
similar to checking that the ABIs used by libraries match. The lack of
such hardware checks currently is the same as there being no checks (in any
arch as far as I can see) that hard and soft float libraries are not mixed.
Therefore all these ideas are new levels of safety for the dynamic linker.

> I suppose that, to the extent that LTO options merging may already cause
> issues when different objects are built with different options, there
> may
> be a case for a -f option meaning "this object uses instruction set
> extensions in functions that may or may not be used at runtime, do not
> automatically use those options for other objects in this link and do
> not
> count those extensions for runtime compatibility checks".

That's an equally complex case but also solvable by clearly annotating the
source with what extensions should be used when building certain functions.
Surely that is the expected way of deploying ifunc optimised C-code.

Regards,
Matthew
Joseph Myers May 16, 2014, 3:29 p.m. UTC | #11
On Fri, 16 May 2014, Matthew Fortune wrote:

> I don't understand the use-case that would want a whole module (and/or
> subset of modules within one library) to freely deploy an ISA extension
> but have no overall marking to that extent. Such global enablement must be

It's the most basic, obvious, longstanding way of using an ISA feature 
conditionally: compile different source files with different options and 
then use "if" conditionals to choose between them.  Support things like 
IFUNCs and "target" attributes as well, sure, but first support the most 
basic form that does not require any special language features at all.

For building source files with different options, in the IFUNC case, see 
for example sysdeps/x86_64/fpu/multiarch/Makefile.

Design options to support that sort of basic usage before getting into 
anything more complicated such as (optional) Solaris-style checks of 
hardware support (which, as can be seen from recent gcc-patches 
discussion, are a persistent cause of problems in the Solaris case; 
people, and GCC testcases, don't expect such checks, so special cases are 
needed for them all over the place).
Richard Sandiford May 25, 2014, 10:18 a.m. UTC | #12
Sorry, catching up on this thread late.

"Joseph S. Myers" <joseph@codesourcery.com> writes:
> On Fri, 16 May 2014, Matthew Fortune wrote:
>
>> I don't understand the use-case that would want a whole module (and/or
>> subset of modules within one library) to freely deploy an ISA extension
>> but have no overall marking to that extent. Such global enablement must be
>
> It's the most basic, obvious, longstanding way of using an ISA feature 
> conditionally: compile different source files with different options and 
> then use "if" conditionals to choose between them.

The MIPS toolchain has never worked like that AFAIK.  Command-line options
decide the ABI of the TU and only compatible TUs can be linked together.
The ABI of the output reflects all requirements of the input TUs.
I think that's the natural behaviour for an architecture with as many
variations as MIPS.

Of the top of my head, I don't remember any bug reports or enhancement
requests asking for a different approach, so I'm not sure the "compile
different objects with different extensions" idiom is used in practice
for MIPS.

Also, I think it would be inconsistent to treat ASEs differently from any
other part of the instruction set.  If DSOs containing a combination
of -mmsa and -mno-msa objects were marked non-msa, then presumably
we should extend that to -march, so that you can do:

   if (running on Loongson 2E)
     loongson_2e_function (...)
   else if (running on Octeon)
     octeon_function (...)
   else if (running on SB-1)
     sb1_function (...)

And if we allow it for those sorts of processor-specific extensions,
presumably we should allow it for the core ISA levels too, with one
function for MIPS IV, one for MIPS64(r1) and one for MIPS64r2.
But I think that would leave us marking every 32-bit object as MIPS
I and every 64-bit object as MIPS III, which seems less safe than what
we have now.

Thanks,
Richard
Joseph Myers June 2, 2014, 5:30 p.m. UTC | #13
On Sun, 25 May 2014, Richard Sandiford wrote:

> "Joseph S. Myers" <joseph@codesourcery.com> writes:
> > On Fri, 16 May 2014, Matthew Fortune wrote:
> >
> >> I don't understand the use-case that would want a whole module (and/or
> >> subset of modules within one library) to freely deploy an ISA extension
> >> but have no overall marking to that extent. Such global enablement must be
> >
> > It's the most basic, obvious, longstanding way of using an ISA feature 
> > conditionally: compile different source files with different options and 
> > then use "if" conditionals to choose between them.
> 
> The MIPS toolchain has never worked like that AFAIK.  Command-line options
> decide the ABI of the TU and only compatible TUs can be linked together.
> The ABI of the output reflects all requirements of the input TUs.
> I think that's the natural behaviour for an architecture with as many
> variations as MIPS.

Linking objects built -mdsp and -mno-dsp, or -march=mips32r2 and 
-march=mips64r2, works for me.  The result does get marked as having the 
appropriate superset instruction set - are you saying there is some 
existing Solaris-style check somewhere (where?) that disallows such a 
combination from executing on hardware lacking the features for all of the 
objects, even if the relevant instructions are only executed 
conditionally?  Or that some other combinations get disallowed at static 
link time?
Richard Sandiford June 2, 2014, 5:43 p.m. UTC | #14
"Joseph S. Myers" <joseph@codesourcery.com> writes:
> On Sun, 25 May 2014, Richard Sandiford wrote:
>
>> "Joseph S. Myers" <joseph@codesourcery.com> writes:
>> > On Fri, 16 May 2014, Matthew Fortune wrote:
>> >
>> >> I don't understand the use-case that would want a whole module (and/or
>> >> subset of modules within one library) to freely deploy an ISA extension
>> >> but have no overall marking to that extent. Such global enablement must be
>> >
>> > It's the most basic, obvious, longstanding way of using an ISA feature 
>> > conditionally: compile different source files with different options and 
>> > then use "if" conditionals to choose between them.
>> 
>> The MIPS toolchain has never worked like that AFAIK.  Command-line options
>> decide the ABI of the TU and only compatible TUs can be linked together.
>> The ABI of the output reflects all requirements of the input TUs.
>> I think that's the natural behaviour for an architecture with as many
>> variations as MIPS.
>
> Linking objects built -mdsp and -mno-dsp, or -march=mips32r2 and 
> -march=mips64r2, works for me.  The result does get marked as having the 
> appropriate superset instruction set

Right, that's I was trying to say.

> - are you saying there is some existing Solaris-style check somewhere
> (where?) that disallows such a combination from executing on hardware
> lacking the features for all of the objects, even if the relevant
> instructions are only executed conditionally?  Or that some other
> combinations get disallowed at static link time?

No, I was arguing that taking the superset was the right behaviour.
You seemed to be saying that we should allow -mmsa to be used for
individual objects without marking the linked output as -mmsa
(because the functions in the -mmsa input object might all be
protected by a runtime check for MSA).

In other words, I was saying -mmsa and -mno-msa should work in the
same way as -mdsp and -mno-dsp: the output is marked as MSA if one
input was compiled with -mmsa.

Thanks,
Richard
Joseph Myers June 2, 2014, 9:59 p.m. UTC | #15
On Mon, 2 Jun 2014, Richard Sandiford wrote:

> > - are you saying there is some existing Solaris-style check somewhere
> > (where?) that disallows such a combination from executing on hardware
> > lacking the features for all of the objects, even if the relevant
> > instructions are only executed conditionally?  Or that some other
> > combinations get disallowed at static link time?
> 
> No, I was arguing that taking the superset was the right behaviour.
> You seemed to be saying that we should allow -mmsa to be used for
> individual objects without marking the linked output as -mmsa
> (because the functions in the -mmsa input object might all be
> protected by a runtime check for MSA).

I'm talking about the overall effect for the toolchain as a whole: that it 
should allow building and running code using such runtime checks, without 
requiring it to use IFUNCs.
Richard Sandiford June 3, 2014, 7:25 a.m. UTC | #16
"Joseph S. Myers" <joseph@codesourcery.com> writes:
> On Mon, 2 Jun 2014, Richard Sandiford wrote:
>
>> > - are you saying there is some existing Solaris-style check somewhere
>> > (where?) that disallows such a combination from executing on hardware
>> > lacking the features for all of the objects, even if the relevant
>> > instructions are only executed conditionally?  Or that some other
>> > combinations get disallowed at static link time?
>> 
>> No, I was arguing that taking the superset was the right behaviour.
>> You seemed to be saying that we should allow -mmsa to be used for
>> individual objects without marking the linked output as -mmsa
>> (because the functions in the -mmsa input object might all be
>> protected by a runtime check for MSA).
>
> I'm talking about the overall effect for the toolchain as a whole: that it 
> should allow building and running code using such runtime checks, without 
> requiring it to use IFUNCs.

OK, but my point was that that AFAIK has never been the case for MIPS
when using command-line options.  You have never been able to compile
a TU with -march=sb1, a TU with -mips64r2 and a TU with -march=octeon
and link them together.  And I think there are good reasons for that.
Similarly if you link -mmdmx and -mno-mdmx code together you get an
-mmdmx output.  

Personally I'm not convinced we should change the behaviour, but if we
did, I think we should do it consistently for all ISAs and ISA extensions
(whether from processor-specific extensions or ASEs).  I don't think MSA
should be a special case.

Thanks,
Richard
Joseph Myers June 3, 2014, 4:34 p.m. UTC | #17
On Tue, 3 Jun 2014, Richard Sandiford wrote:

> Similarly if you link -mmdmx and -mno-mdmx code together you get an
> -mmdmx output.  

But that "-mmdmx output" will run on hardware without the relevant 
feature, if there are runtime checks, I presume (otherwise the MSA case 
would be handled by whatever handles -mmdmx rather than any new checks in 
glibc being proposed that are specific to MSA).
Matthew Fortune June 3, 2014, 5:46 p.m. UTC | #18
Joseph Myers <joseph@codesourcery.com> writes:
> > Similarly if you link -mmdmx and -mno-mdmx code together you get an
> > -mmdmx output.
> 
> But that "-mmdmx output" will run on hardware without the relevant
> feature, if there are runtime checks, I presume

As it stands there are no checks between hardware support and executables
or shared libraries. So (for what I'm calling normal usage) an executable
that includes -mmdmx output will simply crash when run on a non-mdmx
core. I am defining normal usage to be those people who don't know how
to write clever run-time checks. Since there is no way for a userland
process to inspect whether the core supports mdmx currently then there is
no way to improve on this. The introduction of HWCAP bits (specifically
for MSA but not restricted to that) allows us to do better now for my
'normal' usage case and also start to support the idea of a user doing
runtime checks on MIPS.
 
> (otherwise the MSA case
> would be handled by whatever handles -mmdmx rather than any new checks in
> glibc being proposed that are specific to MSA).

We could go back and define HWCAP bits for the other ASEs and perform
appropriate checks for them as well as new ASEs. It seems like a moot
point though given there has been no such dynamic linker checks for MDMX
so far and I believe there is essentially just one implementation of MDMX.
For MIPS3D I'm not sure there are any production implementations. However,
given there is generic support for up to 64 HWCAP bits then assigning one
to each of the pre-existing ASEs won't steal too many of those bits.

To support the scenario that you pointed me towards (x86_64 multiarch I
believe it was) then the only difference for MIPS vs x86_64 would be that
the source wrappers, which are used to rename functions and define special
sections, would also need to have a pragma or other source level directive
to enable MSA for the remaining functions in the file. This allows us to
differentiate between the 'globally enable a feature' via command line
and 'hide the usage of the feature' for user-supplied runtime checks.

The thing which MIPS has and many(/all?) other architectures lack is
information stored in an executable or DSO which accurately states their
requirements. This is the basis for suggestion we should do whole
executable and whole DSO checks for MIPS (as well as supporting the user
supplied runtime detection case).

Regards,
Matthew
Maciej W. Rozycki June 3, 2014, 9:12 p.m. UTC | #19
On Tue, 3 Jun 2014, Matthew Fortune wrote:

> For MIPS3D I'm not sure there are any production implementations. However,
> given there is generic support for up to 64 HWCAP bits then assigning one
> to each of the pre-existing ASEs won't steal too many of those bits.

 For the record the Broadcom SiByte SB-1 processor is a production 
implementation of the MIPS-3D ASE.

  Maciej

Patch
diff mbox

diff --git a/elf/dl-load.c b/elf/dl-load.c
index cfa7f25..201bf18 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1697,6 +1697,11 @@  open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
 	    }
 	}
 
+      if (!__glibc_likely (
+	     elf_machine_phdr_check (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
+				     fd, loader)))
+	goto close_and_out;
+
       /* 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..8d83b47 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,93 @@  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_DSPR2		0x00000002 /* DSP R2 ASE.  */
+#define AFL_ASE_EVA		0x00000004 /* Enhanced VA Scheme.  */
+#define AFL_ASE_MCU		0x00000008 /* MCU (MicroController) ASE.  */
+#define AFL_ASE_MDMX		0x00000010 /* MDMX ASE.  */
+#define AFL_ASE_MIPS3D		0x00000020 /* MIPS-3D ASE.  */
+#define AFL_ASE_MT		0x00000040 /* MT ASE.  */
+#define AFL_ASE_SMARTMIPS	0x00000080 /* SmartMIPS ASE.  */
+#define AFL_ASE_VIRT		0x00000100 /* VZ ASE.  */
+#define AFL_ASE_MSA		0x00000200 /* MSA ASE.  */
+#define AFL_ASE_MIPS16		0x00000400 /* MIPS16 ASE.  */
+#define AFL_ASE_MICROMIPS	0x00000800 /* MICROMIPS ASE.  */
+#define AFL_ASE_XPA		0x00001000 /* XPA ASE.  */
+
+/* Values for the isa_ext word of an ABI flags structure.  */
+
+#define AFL_EXT_XLR		1   /* RMI Xlr instruction.  */
+#define AFL_EXT_OCTEON2		2   /* Cavium Networks Octeon2.  */
+#define AFL_EXT_OCTEONP		3   /* Cavium Networks OcteonP.  */
+#define AFL_EXT_LOONGSON_3A	4   /* Loongson 3A.  */
+#define AFL_EXT_OCTEON		5   /* Cavium Networks Octeon.  */
+#define AFL_EXT_5900		6   /* MIPS R5900 instruction.  */
+#define AFL_EXT_4650		7   /* MIPS R4650 instruction.  */
+#define AFL_EXT_4010		8   /* LSI R4010 instruction.  */
+#define AFL_EXT_4100		9   /* NEC VR4100 instruction.  */
+#define AFL_EXT_3900		10  /* Toshiba R3900 instruction.  */
+#define AFL_EXT_10000		11  /* MIPS R10000 instruction.  */
+#define AFL_EXT_SB1		12  /* Broadcom SB-1 instruction.  */
+#define AFL_EXT_4111		13  /* NEC VR4111/VR4181 instruction.  */
+#define AFL_EXT_4120		14  /* NEC VR4120 instruction.  */
+#define AFL_EXT_5400		15  /* NEC VR5400 instruction.  */
+#define AFL_EXT_5500		16  /* NEC VR5500 instruction.  */
+#define AFL_EXT_LOONGSON_2E	17  /* ST Microelectronics Loongson 2E.  */
+#define AFL_EXT_LOONGSON_2F	18  /* ST Microelectronics 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/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
index 997c860..9770e3d 100644
--- a/sysdeps/aarch64/dl-machine.h
+++ b/sysdeps/aarch64/dl-machine.h
@@ -32,6 +32,15 @@  elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
   return ehdr->e_machine == EM_AARCH64;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT. */
 static inline ElfW(Addr) __attribute__ ((unused))
diff --git a/sysdeps/alpha/dl-machine.h b/sysdeps/alpha/dl-machine.h
index 63db19c..9e65706 100644
--- a/sysdeps/alpha/dl-machine.h
+++ b/sysdeps/alpha/dl-machine.h
@@ -42,6 +42,15 @@  elf_machine_matches_host (const Elf64_Ehdr *ehdr)
   return ehdr->e_machine == EM_ALPHA;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* Return the link-time address of _DYNAMIC.  The multiple-got-capable
    linker no longer allocates the first .got entry for this.  But not to
    worry, no special tricks are needed.  */
diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
index 899b256..9bf4b89 100644
--- a/sysdeps/arm/dl-machine.h
+++ b/sysdeps/arm/dl-machine.h
@@ -38,6 +38,16 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
 }
 
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  */
 static inline Elf32_Addr __attribute__ ((unused))
diff --git a/sysdeps/generic/dl-machine.h b/sysdeps/generic/dl-machine.h
index d7a2b60..c036ec3 100644
--- a/sysdeps/generic/dl-machine.h
+++ b/sysdeps/generic/dl-machine.h
@@ -33,6 +33,14 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
     }
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
 
 /* Return the link-time address of _DYNAMIC.  */
 static inline Elf32_Addr
diff --git a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h
index ba21f07..423d51c 100644
--- a/sysdeps/hppa/dl-machine.h
+++ b/sysdeps/hppa/dl-machine.h
@@ -72,6 +72,15 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
   return ehdr->e_machine == EM_PARISC;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* Return the link-time address of _DYNAMIC.  */
 static inline Elf32_Addr
 elf_machine_dynamic (void) __attribute__ ((const));
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
index 368bee2..6541c20 100644
--- a/sysdeps/i386/dl-machine.h
+++ b/sysdeps/i386/dl-machine.h
@@ -34,6 +34,16 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
 }
 
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
+
 #ifdef PI_STATIC_AND_HIDDEN
 
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
diff --git a/sysdeps/ia64/dl-machine.h b/sysdeps/ia64/dl-machine.h
index 853e6fd..dfc6377 100644
--- a/sysdeps/ia64/dl-machine.h
+++ b/sysdeps/ia64/dl-machine.h
@@ -54,6 +54,14 @@  elf_machine_matches_host (const Elf64_Ehdr *ehdr)
   return ehdr->e_machine == EM_IA_64;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
 
 /* Return the link-time address of _DYNAMIC.  */
 static inline Elf64_Addr __attribute__ ((unused, const))
diff --git a/sysdeps/m68k/dl-machine.h b/sysdeps/m68k/dl-machine.h
index 3ec9862..68f62d7 100644
--- a/sysdeps/m68k/dl-machine.h
+++ b/sysdeps/m68k/dl-machine.h
@@ -33,6 +33,16 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
 }
 
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
+
 /* Return the link-time address of _DYNAMIC.
    This must be inlined in a function which uses global data.  */
 static inline Elf32_Addr
diff --git a/sysdeps/microblaze/dl-machine.h b/sysdeps/microblaze/dl-machine.h
index 848e822..be7fe49 100644
--- a/sysdeps/microblaze/dl-machine.h
+++ b/sysdeps/microblaze/dl-machine.h
@@ -31,6 +31,15 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
   return (ehdr->e_machine == EM_MICROBLAZE);
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  This must be inlined in a function which
    uses global data.  */
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..86ca168 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,247 @@  elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
     }
 }
 
+/* Search the program headers for the ABI Flags.  */
+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));
+}
+
+/* Obtain the current FR mode setting.  */
+
+static inline int
+get_frmode (void)
+{
+  int frmode;
+  asm volatile ("cfc1 %0,$1\n": "=r"(frmode));
+  return frmode;
+}
+#endif
+
+/* Return a description of the specified floating-point ABI.  */
+
+static const char *
+mips_fp_abi_string (int fpabi)
+{
+  switch (fpabi)
+    {
+    case Val_GNU_MIPS_ABI_FP_ANY:
+      return "Hard or soft float";
+    case Val_GNU_MIPS_ABI_FP_DOUBLE:
+      return "Hard float (double precision)";
+    case Val_GNU_MIPS_ABI_FP_SINGLE:
+      return "Hard float (single precision)";
+    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";
+    }
+}
+
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  This verifies that floating-point ABIs are compatible and
+   re-configures the hardware FR mode if necessary.  */
+
+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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+		_dl_debug_printf ("   Unable to read PT_MIPS_ABIFLAGS\n");
+	      return 0;
+	    }
+	}
+      if (size < sizeof (Elf_ABIFlags_v0))
+	{
+	  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+	    _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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+		      _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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+	      _dl_debug_printf ("   uses %s, already loaded %s\n",
+				mips_fp_abi_string (req_abi),
+				mips_fp_abi_string (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 frmode;
+	  /* Obtain current FR mode via UFR.  */
+	  frmode = get_frmode ();
+	  if (frmode == 0 && req_abi == Val_GNU_MIPS_ABI_FP_64)
+	    {
+	      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+		_dl_debug_printf ("   setting FR mode to 1\n");
+	      switch_frmode_to (1);
+	    }
+	  else if (frmode == 1 && req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE)
+	    {
+	      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+		_dl_debug_printf ("   setting FR mode to 0\n");
+	      switch_frmode_to (0);
+	    }
+
+	  frmode = get_frmode ();
+	  if ((frmode == 0 && req_abi == Val_GNU_MIPS_ABI_FP_64)
+	      || (frmode == 1 && req_abi == Val_GNU_MIPS_ABI_FP_DOUBLE))
+	    _dl_signal_error (0, map->l_name, NULL,
+			      "hardware failed to set FR mode");
+	}
+      else if (req_abi == Val_GNU_MIPS_ABI_FP_64)
+	{
+	  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+	    _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/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
index 23b610f..2572eea 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.h
+++ b/sysdeps/powerpc/powerpc32/dl-machine.h
@@ -36,6 +36,15 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
   return ehdr->e_machine == EM_PPC;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* Return the value of the GOT pointer.  */
 static inline Elf32_Addr * __attribute__ ((const))
 ppc_got (void)
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index bc99183..f0434b2 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -80,6 +80,14 @@  elf_host_tolerates_class (const Elf64_Ehdr *ehdr)
   return ehdr->e_ident[EI_CLASS] == ELFCLASS32;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
 
 /* Return the run-time load address of the shared object, assuming it
    was originally linked at zero.  */
diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
index 4fd2745..bbf7ba1 100644
--- a/sysdeps/s390/s390-32/dl-machine.h
+++ b/sysdeps/s390/s390-32/dl-machine.h
@@ -46,6 +46,16 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
 }
 
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  This must be inlined in a function which
    uses global data.  */
diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
index 2f37169..e480261 100644
--- a/sysdeps/s390/s390-64/dl-machine.h
+++ b/sysdeps/s390/s390-64/dl-machine.h
@@ -41,6 +41,15 @@  elf_machine_matches_host (const Elf64_Ehdr *ehdr)
 	 && ehdr->e_ident[EI_CLASS] == ELFCLASS64;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  This must be inlined in a function which
    uses global data.  */
diff --git a/sysdeps/sh/dl-machine.h b/sysdeps/sh/dl-machine.h
index 4f3db89..ba2223e 100644
--- a/sysdeps/sh/dl-machine.h
+++ b/sysdeps/sh/dl-machine.h
@@ -33,6 +33,16 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
 }
 
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  This must be inlined in a function which
    uses global data.  */
diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
index e7d31b4..65a849f 100644
--- a/sysdeps/sparc/sparc32/dl-machine.h
+++ b/sysdeps/sparc/sparc32/dl-machine.h
@@ -48,6 +48,15 @@  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
     return 0;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* We have to do this because elf_machine_{dynamic,load_address} can be
    invoked from functions that have no GOT references, and thus the compiler
    has no obligation to load the PIC register.  */
diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
index ef4ad4c..05f2669 100644
--- a/sysdeps/sparc/sparc64/dl-machine.h
+++ b/sysdeps/sparc/sparc64/dl-machine.h
@@ -37,6 +37,15 @@  elf_machine_matches_host (const Elf64_Ehdr *ehdr)
   return ehdr->e_machine == EM_SPARCV9;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
 /* We have to do this because elf_machine_{dynamic,load_address} can be
    invoked from functions that have no GOT references, and thus the compiler
    has no obligation to load the PIC register.  */
diff --git a/sysdeps/tile/dl-machine.h b/sysdeps/tile/dl-machine.h
index d686a65..8fa86d2 100644
--- a/sysdeps/tile/dl-machine.h
+++ b/sysdeps/tile/dl-machine.h
@@ -53,6 +53,16 @@  elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
 }
 
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
+
+
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  This must be inlined in a function which
    uses global data.  */
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/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
index 8df04a9..290c405 100644
--- a/sysdeps/x86_64/dl-machine.h
+++ b/sysdeps/x86_64/dl-machine.h
@@ -34,6 +34,14 @@  elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
   return ehdr->e_machine == EM_X86_64;
 }
 
+/* Return nonzero iff ELF program headers are compatible with the running
+   host.  */
+static inline int
+elf_machine_phdr_check (const ElfW(Phdr) *phdr, ElfW(Half) phnum,
+			char * buf, int len, int fd, struct link_map * map)
+{
+  return 1;
+}
 
 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
    first element of the GOT.  This must be inlined in a function which