From patchwork Thu Sep 3 22:03:49 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Faraz Shahbazker X-Patchwork-Id: 8571 Received: (qmail 99826 invoked by alias); 3 Sep 2015 22:04:04 -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 99816 invoked by uid 89); 3 Sep 2015 22:04:03 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.3 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mailapp01.imgtec.com Message-ID: <55E8C3C5.8000107@imgtec.com> Date: Thu, 3 Sep 2015 15:03:49 -0700 From: Faraz Shahbazker User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.2.0 MIME-Version: 1.0 To: "libc-alpha@sourceware.org" CC: Matthew Fortune , Robert Suchanek Subject: [RFC 2/2] Update prototype of IFUNC resolver for MIPS References: <87k2tdn5xt.fsf@googlemail.com> <55BFC10F.2050503@imgtec.com> <87k2tapwq0.fsf@googlemail.com> <55CE5217.5020902@imgtec.com> <87io8f2gc9.fsf@googlemail.com> <55D23368.1070705@imgtec.com> <87io8dhegb.fsf@googlemail.com> <55D269D2.2030208@imgtec.com> <87si7h6oyv.fsf@googlemail.com> <55D7A3C5.8050600@imgtec.com> <87vbc7u1df.fsf@googlemail.com> In-Reply-To: <87vbc7u1df.fsf@googlemail.com> Comments are invited on the prototype of the IFUNC resolver for MIPS. We want something flexible enough to handle current requirements and extensions. This patch, from RobertS, provides the resolver with 2 HWCAP registers and a callback control function. The callback currently provides the ability to toggle/switch between FP ABI modes. Caveat: specific HWCAP values for MIPS are not in place yet. ChangeLog: * sysdeps/mips/Makefile (sysdep_headers): Add sys/dlifnctl.h * sysdeps/mips/dl-ifunc-ctrl.h: New file. (dl_ifunc_control): New function. * sysdeps/mips/dl-irel.h Include dl-ifunc-ctrl.h Change resolver prototype to use 2 HWCAPs and dl_ifunc_control callback. * sysdeps/mips/dl-machine-reject-phdr.h (REJECT): Replace _dl_debug_printf with GLRO(dl_debug_printf) (cached_fpabi_reject_phdr_p): Restricted to SHARED build. (ifunc_fpabi): New variable, track the first fpabi request. (dl_reject_fpabi_req): New function with using compat logic from elf_machine_reject_phdr_p. (elf_machine_reject_phdr_p): Refactored to call dl_reject_fpabi_req; all logic related to compatibility check moved to dl_reject_fpabi_req. Replace _dl_debug_printf with GLRO(dl_debug_printf). * sysdeps/mips/sys/dlifnctl.h (R_MIPS_IRELATIVE): New file. (DL_IFUNC_MIPS_FR_MODE_SWITCH DL_IFUNC_MIPS_FP_ABI): New operation request modes for IFUNC control callback. --- sysdeps/mips/Makefile | 2 +- sysdeps/mips/dl-ifunc-ctrl.h | 90 ++++++++++++++++++++++ sysdeps/mips/dl-irel.h | 5 +- sysdeps/mips/dl-machine-reject-phdr.h | 131 +++++++++++++++++++++++---------- sysdeps/mips/sys/dlifnctl.h | 26 +++++++ 5 files changed, 214 insertions(+), 40 deletions(-) create mode 100644 sysdeps/mips/dl-ifunc-ctrl.h create mode 100644 sysdeps/mips/sys/dlifnctl.h diff --git a/sysdeps/mips/Makefile b/sysdeps/mips/Makefile index 463b121..a598b57 100644 --- a/sysdeps/mips/Makefile +++ b/sysdeps/mips/Makefile @@ -1,6 +1,6 @@ ifeq ($(subdir),misc) sysdep_headers += regdef.h fpregdef.h sys/regdef.h sys/fpregdef.h \ - sys/asm.h sgidefs.h + sys/asm.h sys/dlifnctl.h sgidefs.h endif ifeq ($(subdir),setjmp) diff --git a/sysdeps/mips/dl-ifunc-ctrl.h b/sysdeps/mips/dl-ifunc-ctrl.h new file mode 100644 index 0000000..daa95b3 --- /dev/null +++ b/sysdeps/mips/dl-ifunc-ctrl.h @@ -0,0 +1,90 @@ +/* Machine-dependent callback control function for ifunc resolver functions. + Copyright (C) 2015 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 _DL_IFUNC_CTRL_H +#define _DL_IFUNC_CTRL_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +static int __attribute_used__ +dl_ifunc_control (int operation, int value) +{ + int in_abi = -1; + + switch (operation) + { + case DL_IFUNC_MIPS_FR_MODE_SWITCH: + if (value == 0) + in_abi = Val_GNU_MIPS_ABI_FP_DOUBLE; + else if (value == 1) + in_abi = Val_GNU_MIPS_ABI_FP_64; + else + { + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS)) + GLRO(dl_debug_printf) (" requested invalid FR mode: 0x%x\n", + value); +#if _MIPS_SIM == _ABIO32 +# if HAVE_PRCTL_FP_MODE + return __prctl (PR_GET_FP_MODE); +# else + /* If the PR_GET_FP_MODE is not supported then only FR0 + is available. */ + return 0; +# endif +#else + return 1; +#endif + } + /* Fall through. */ + case DL_IFUNC_MIPS_FP_ABI: + if (in_abi == -1) + in_abi = value; + + /* Try the new requirement as if it was an object. */ + if (!dl_reject_fpabi_req (in_abi)) + { + /* Remember the choice for the last compabitle FP ABI. */ + ifunc_fpabi = in_abi; + } +#if _MIPS_SIM == _ABIO32 +# if HAVE_PRCTL_FP_MODE + return __prctl (PR_GET_FP_MODE); +# else + return 0; +# endif +#else + return 1; +#endif + default: +#ifdef SHARED + GLRO(dl_debug_printf) ("unrecognized ifunc operation\n"); + _exit (127); +#else + _dl_fatal_printf ("unrecognized ifunc operation in static binary\n"); +#endif + /* NOT REACHED */ + } +} +#endif diff --git a/sysdeps/mips/dl-irel.h b/sysdeps/mips/dl-irel.h index 47f3257..2a42380 100644 --- a/sysdeps/mips/dl-irel.h +++ b/sysdeps/mips/dl-irel.h @@ -26,6 +26,7 @@ #include #include #include +#include #define ELF_MACHINE_IREL 1 @@ -43,7 +44,9 @@ elf_ifunc_invoke (ElfW(Addr) addr) (unsigned long int)t_addr); } - return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap)); + return ((ElfW(Addr) (*) (unsigned long int, unsigned long int, + int (int, int))) addr) + (GLRO(dl_hwcap), GLRO(dl_hwcap2), dl_ifunc_control); } /* Allow either R_MIPS_RELATIVE or the nop R_MIPS_NONE. */ diff --git a/sysdeps/mips/dl-machine-reject-phdr.h b/sysdeps/mips/dl-machine-reject-phdr.h index b09df30..6233fae 100644 --- a/sysdeps/mips/dl-machine-reject-phdr.h +++ b/sysdeps/mips/dl-machine-reject-phdr.h @@ -32,7 +32,7 @@ #define REJECT(str, args...) \ { \ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) \ - _dl_debug_printf (str, ##args); \ + GLRO(dl_debug_printf) (str, ##args); \ return true; \ } @@ -49,6 +49,7 @@ find_mips_abiflags (const ElfW(Phdr) *phdr, ElfW(Half) phnum) return NULL; } +#ifdef SHARED /* Cache the FP ABI value from the PT_MIPS_ABIFLAGS program header. */ static bool @@ -82,6 +83,7 @@ cached_fpabi_reject_phdr_p (struct link_map *l) } return false; } +#endif /* Return a description of the specified floating-point ABI. */ @@ -145,23 +147,22 @@ static const struct abi_req reqs[Val_GNU_MIPS_ABI_FP_MAX + 1] = static const struct abi_req none_req = { true, true, true, false, true }; -/* Return true iff ELF program headers are incompatible with the running - host. This verifies that floating-point ABIs are compatible and - re-configures the hardware mode if necessary. This code handles both the - DT_NEEDED libraries and the dlopen'ed libraries. It also accounts for the - impact of dlclose. */ +/* FP ABI requirement for ifunc with callback. The new mode switch can only + be requested once. */ + +static int ifunc_fpabi = -1; + +/* Return true iff that the new FP ABI requirement conflicts with any + currently loaded object. */ static bool __attribute_used__ -elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, - const char *buf, size_t len, struct link_map *map, - int fd) +dl_reject_fpabi_req (int in_abi) { - const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum); +#ifdef SHARED struct link_map *l; Lmid_t nsid; - int in_abi = -1; +#endif struct abi_req in_req; - Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL; bool perfect_match = false; #if _MIPS_SIM == _ABIO32 unsigned int cur_mode = -1; @@ -173,30 +174,6 @@ elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, # endif #endif - /* Read the attributes section. */ - if (ph != NULL) - { - ElfW(Addr) size = ph->p_filesz; - - if (ph->p_offset + size <= len) - mips_abiflags = (Elf_MIPS_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) - REJECT (" unable to read PT_MIPS_ABIFLAGS\n"); - } - - if (size < sizeof (Elf_MIPS_ABIFlags_v0)) - REJECT (" contains malformed PT_MIPS_ABIFLAGS\n"); - - if (__glibc_unlikely (mips_abiflags->flags2 != 0)) - REJECT (" unknown MIPS.abiflags flags2: %u\n", mips_abiflags->flags2); - - in_abi = mips_abiflags->fp_abi; - } - /* ANY is compatible with anything. */ perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY); @@ -207,6 +184,7 @@ elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, /* Obtain the initial requirements. */ in_req = (in_abi == -1) ? none_req : reqs[in_abi]; +#ifdef SHARED /* Check that the new requirement does not conflict with any currently loaded object. */ for (nsid = 0; nsid < DL_NNS; ++nsid) @@ -266,6 +244,37 @@ elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, fpabi_string (in_abi), fpabi_string (l->l_mach.fpabi)); } +#endif + + /* Check the compability of the FP ABI requested in ifunc callback with + the loaded objects. */ + if (ifunc_fpabi != -1) + { + struct abi_req existing_req; + + /* Found a perfect match, success. */ + perfect_match |= (in_abi == ifunc_fpabi); + + existing_req = reqs[ifunc_fpabi]; + + /* Merge requirements. */ + in_req.soft &= existing_req.soft; + in_req.single &= existing_req.single; + in_req.fr0 &= existing_req.fr0; + in_req.fr1 &= existing_req.fr1; + in_req.fre &= existing_req.fre; + + /* If there is at least one mode which is still usable then the new + object can be loaded. */ + if (in_req.single || in_req.soft || in_req.fr1 || in_req.fr0 + || in_req.fre) + { + } + else + REJECT (" uses %s, ifunc already loaded %s\n", + fpabi_string (in_abi), + fpabi_string (ifunc_fpabi)); + } #if _MIPS_SIM == _ABIO32 /* At this point we know that the newly loaded object is compatible with all @@ -274,8 +283,9 @@ elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, && !perfect_match) { if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) - _dl_debug_printf (" needs %s%s mode\n", in_req.fr0 ? "FR0 or " : "", - (in_req.fre && !in_req.fr1) ? "FRE" : "FR1"); + GLRO (dl_debug_printf) (" needs %s%s mode\n", + in_req.fr0 ? "FR0 or " : "", + (in_req.fre && !in_req.fr1) ? "FRE" : "FR1"); /* If the PR_GET_FP_MODE is not supported then only FR0 is available. If the overall requirements cannot be met by FR0 then reject the @@ -323,4 +333,49 @@ elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, return false; } +/* Return true iff ELF program headers are incompatible with the running + host. This verifies that floating-point ABIs are compatible and + re-configures the hardware mode if necessary. This code handles both the + DT_NEEDED libraries and the dlopen'ed libraries. It also accounts for the + impact of dlclose. */ + +static bool __attribute_used__ +elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, + const char *buf, size_t len, struct link_map *map, + int fd) +{ + const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum); + int in_abi = -1; + Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL; + + /* Read the attributes section. */ + if (ph != NULL) + { + ElfW(Addr) size = ph->p_filesz; + + if (ph->p_offset + size <= len) + mips_abiflags = (Elf_MIPS_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) + REJECT (" unable to read PT_MIPS_ABIFLAGS\n"); + } + + if (size < sizeof (Elf_MIPS_ABIFlags_v0)) + REJECT (" contains malformed PT_MIPS_ABIFLAGS\n"); + + if (__glibc_unlikely (mips_abiflags->flags2 != 0)) + REJECT (" unknown MIPS.abiflags flags2: %u\n", mips_abiflags->flags2); + + in_abi = mips_abiflags->fp_abi; + } + + if (dl_reject_fpabi_req (in_abi)) + return true; + + return false; +} + #endif /* dl-machine-reject-phdr.h */ diff --git a/sysdeps/mips/sys/dlifnctl.h b/sysdeps/mips/sys/dlifnctl.h new file mode 100644 index 0000000..6e21fcf --- /dev/null +++ b/sysdeps/mips/sys/dlifnctl.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2015 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_DLIFNCTL_H +#define _SYS_DLIFNCTL_H + +#include + +#define DL_IFUNC_MIPS_FR_MODE_SWITCH 0 /* Request FR mode change. */ +#define DL_IFUNC_MIPS_FP_ABI 1 /* Request FR mode for FP ABI. */ + +#endif