Message ID | 6D39441BF12EF246A7ABCE6654B0235352F38D@LEMAIL01.le.imgtec.org |
---|---|
State | New |
Headers | show |
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 >
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.
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
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).
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
> 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
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?
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
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".
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
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).
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
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?
"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
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.
"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
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).
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
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
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