From patchwork Wed May 14 14:46:37 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Fortune X-Patchwork-Id: 909 Return-Path: X-Original-To: siddhesh@wilcox.dreamhost.com Delivered-To: siddhesh@wilcox.dreamhost.com Received: from homiemail-mx22.g.dreamhost.com (mx2.sub5.homie.mail.dreamhost.com [208.113.200.128]) by wilcox.dreamhost.com (Postfix) with ESMTP id 40AFB36007B for ; Wed, 14 May 2014 07:46:52 -0700 (PDT) Received: by homiemail-mx22.g.dreamhost.com (Postfix, from userid 14307373) id DE99D5892538; Wed, 14 May 2014 07:46:51 -0700 (PDT) X-Original-To: glibc@patchwork.siddhesh.in Delivered-To: x14307373@homiemail-mx22.g.dreamhost.com Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by homiemail-mx22.g.dreamhost.com (Postfix) with ESMTPS id 9A85457FF24E for ; Wed, 14 May 2014 07:46:51 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id :content-type:content-transfer-encoding:mime-version; q=dns; s= default; b=e6/nm7V4G5Cv563uhNk7MPQu/wr6qA3Vj+A543qj0cJzFicQHgv/S MALLkJF5roGfuxcyuzjxDEYzpueKS9u5fScAVXiOVeOlvfIT/ptanzeL93VX/565 CjyNM3aQ/gnAk30RtazB5PF5MqHmdqND9qLFADP4OS/ptF6MT+idqw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id :content-type:content-transfer-encoding:mime-version; s=default; bh=4JL+uRvFn6GqgOffHwiaTtndoK4=; b=BYqP51GnWvI0Pov6sziuvegM5cFp cb8lWRLx5r6CY77xj9yzN/M7px0sPORK2QOlEdq+qsPxVvkmK7qNxOQC5XUTrHBr 3P0su2T1qFysZdkr6+RHO6NkRoFzI1EJkqlYEMuZR7M+wLWBcGU/geoZUirIMvDM AmtQ/DnUo3wI504= Received: (qmail 21584 invoked by alias); 14 May 2014 14:46:49 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 21567 invoked by uid 89); 14 May 2014 14:46:48 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.5 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mailapp01.imgtec.com From: Matthew Fortune To: Joseph Myers CC: Will Newton , Andrew Pinski , Richard Sandiford , Rich Fuhler , "macro@codesourcery.com" , "libc-alpha@sourceware.org" Subject: [PATCHv2, MIPS] Add support for O32 FPXX and program header based ABI information Date: Wed, 14 May 2014 14:46:37 +0000 Message-ID: <6D39441BF12EF246A7ABCE6654B0235352F38D@LEMAIL01.le.imgtec.org> MIME-Version: 1.0 X-DH-Original-To: glibc@patchwork.siddhesh.in Hi Joseph, I've updated the patch based on the comments. I couldn't change the error message about failing to set the FR mode as suggested because dl_signal_error does not support formatters. I did however add debug diagnostics to state what mode change was being attempted and this can be used in conjunction with the error to find out what was happening. The diagnostic will also be useful simply to know when a module triggers a successful mode change too. The AFL* flags have been updated in line with changes to binutils FPXX support. The HAVE_MIPS_MODULE_FPXX_DIRECTIVE macro/configure test is no longer needed owing to a change in how the assembler handles inferring an FP ABI. I have not yet tested all the FP ABI combinations but have covered the ones which are related to FPXX. I am working through the rest as well as n32/n64 ABIs. I'm not sure what I should do for testing/checking the changes to all the dl-machine.h files... I built an aarch64 version of glibc to check at least one still builds and the same code is placed in all dl-machine.h files. Each file appears to have a different whitespace policy so I left either one or two newlines between functions depending on the file. I would also like to add in another feature to check for the presence of MSA in an object and reject it if HWCAP_MIPS_MSA is not set. With that in place users can construct MSA and non-MSA optimised libraries and place the MSA library first in the search path and get the best supported by the host. This is possible because the MSA extension makes no changes to the calling convention. Does that sound OK? Regards, Matthew 2014-05-14 Matthew Fortune * 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; +} + /* 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 + . */ + +#ifndef _SYS_AUXV_H +# error "Never include directly; use 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 #include #include +#include /* 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 #include +#define _SYS_AUXV_H 1 +#include + #ifdef __ASSEMBLER__ #include 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