From patchwork Wed Jun 12 10:41:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Szabolcs Nagy X-Patchwork-Id: 33091 Received: (qmail 17092 invoked by alias); 12 Jun 2019 10:41:41 -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 17074 invoked by uid 89); 12 Jun 2019 10:41:41 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.4 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.1 spammy=3, 6 X-HELO: EUR03-AM5-obe.outbound.protection.outlook.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=FlJoBvtaAermAYmqHxQdrBeAvL7R+ZKRsSejvO7myjg=; b=4mEXXx9NQn8rNZ7b/xzsg9Mu+cBttwk/LEIKtudeiSFHFmFmxQCsTnKICDZN6Z19FfGgzzqA5NGK/k1Wi6As39aCDTJT0huNHPp7zzEf7V/+VIm5SXFVMe3CiOxrP1FQNwjpEoX6GZuIMdEXnKf/7LG7u+69QBHBkztIDkKAGtw= From: Szabolcs Nagy To: GNU C Library CC: nd , Florian Weimer Subject: Re: [RFC PATCH] aarch64: new ifunc resolver ABI Date: Wed, 12 Jun 2019 10:41:35 +0000 Message-ID: References: In-Reply-To: user-agent: Mozilla/5.0 (X11; Linux aarch64; rv:60.0) Gecko/20100101 Thunderbird/60.4.0 authentication-results: spf=none (sender IP is ) smtp.mailfrom=Szabolcs.Nagy@arm.com; x-ms-exchange-purlcount: 1 x-ms-oob-tlc-oobclassifiers: OLM:8882; received-spf: None (protection.outlook.com: arm.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 MIME-Version: 1.0 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: Szabolcs.Nagy@arm.com On 12/06/2019 11:09, Szabolcs Nagy wrote: > Passing a second argument to the ifunc resolver allows accessing > AT_HWCAP2 values from the resolver. AArch64 will start using AT_HWCAP2 > on linux because for ilp32 to remain compatible with lp64 ABI no more > than 32bit hwcap flags can be in AT_HWCAP which is already used up. > > Currently the relocation ordering logic does not guarantee that ifunc > resolvers can call libc apis or access libc objects, so only the > resolver arguments and runtime environment dependent instructions can > be used to do the dispatch (this affects ifunc resolvers outside of > the libc). > > Since ifunc resolver is target specific and only supposed to be > called by the dynamic linker, the call ABI can be changed in a > backward compatible way: > > Old call ABI passed hwcap as uint64_t, new abi sets the > _IFUNC_ARG_HWCAP flag in the hwcap and passes a second argument > that's a pointer to an extendible struct. A resolver has to check > the _IFUNC_ARG_HWCAP flag before accessing the second argument. > > The new sys/ifunc.h installed header has the definitions for the > new ABI, everything is in the implementation reserved namespace. > > An alternative approach is to try to support extern calls from ifunc > resolvers such as getauxval, but that seems non-trivial > https://sourceware.org/ml/libc-alpha/2017-01/msg00468.html > > 2019-06-12 Szabolcs Nagy > > * sysdeps/aarch64/Makefile: Install sys/ifunc.h and add tests. > * sysdeps/aarch64/dl-irel.h (elf_ifunc_invoke): Update to new ABI. > * sysdeps/aarch64/sys/ifunc.h: New file. > * sysdeps/aarch64/tst-ifunc-arg-1.c: New file. > * sysdeps/aarch64/tst-ifunc-arg-2.c: New file. > now with the correct patch attached. diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile index 94baaf52dd..9cb141004d 100644 --- a/sysdeps/aarch64/Makefile +++ b/sysdeps/aarch64/Makefile @@ -3,6 +3,8 @@ long-double-fcts = yes ifeq ($(subdir),elf) sysdep-dl-routines += tlsdesc dl-tlsdesc gen-as-const-headers += dl-link.sym + +tests-internal += tst-ifunc-arg-1 tst-ifunc-arg-2 endif ifeq ($(subdir),csu) @@ -16,3 +18,7 @@ endif ifeq ($(subdir),math) CPPFLAGS += -I../soft-fp endif + +ifeq ($(subdir),misc) +sysdep_headers += sys/ifunc.h +endif diff --git a/sysdeps/aarch64/dl-irel.h b/sysdeps/aarch64/dl-irel.h index 4f669e70d7..8db6ef57dd 100644 --- a/sysdeps/aarch64/dl-irel.h +++ b/sysdeps/aarch64/dl-irel.h @@ -24,6 +24,7 @@ #include #include #include +#include #define ELF_MACHINE_IRELA 1 @@ -31,7 +32,13 @@ static inline ElfW(Addr) __attribute ((always_inline)) elf_ifunc_invoke (ElfW(Addr) addr) { - return ((ElfW(Addr) (*) (uint64_t)) (addr)) (GLRO(dl_hwcap)); + __ifunc_arg_t arg; + + arg._size = sizeof (arg); + arg._hwcap = GLRO(dl_hwcap); + arg._hwcap2 = GLRO(dl_hwcap2); + return ((ElfW(Addr) (*) (uint64_t, const __ifunc_arg_t *)) (addr)) + (GLRO(dl_hwcap) | _IFUNC_ARG_HWCAP, &arg); } static inline void diff --git a/sysdeps/aarch64/sys/ifunc.h b/sysdeps/aarch64/sys/ifunc.h new file mode 100644 index 0000000000..ef200e9f26 --- /dev/null +++ b/sysdeps/aarch64/sys/ifunc.h @@ -0,0 +1,42 @@ +/* Definitions used by AArch64 indirect function resolvers. + Copyright (C) 2019 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_IFUNC_H +#define _SYS_IFUNC_H + +/* A second argument is passed to the ifunc resolver. */ +#define _IFUNC_ARG_HWCAP (1ULL << 62) + +/* The prototype of a gnu indirect function resolver on AArch64 is + + ElfW(Addr) ifunc_resolver (uint64_t, const __ifunc_arg_t *); + + the first argument should have the _IFUNC_ARG_HWCAP bit set and + the remaining bits should match the AT_HWCAP settings. */ + +/* Second argument to an ifunc resolver. */ +struct __ifunc_arg_t +{ + unsigned long _size; /* Size of the struct, so it can grow. */ + unsigned long _hwcap; + unsigned long _hwcap2; +}; + +typedef struct __ifunc_arg_t __ifunc_arg_t; + +#endif diff --git a/sysdeps/aarch64/tst-ifunc-arg-1.c b/sysdeps/aarch64/tst-ifunc-arg-1.c new file mode 100644 index 0000000000..cedd987030 --- /dev/null +++ b/sysdeps/aarch64/tst-ifunc-arg-1.c @@ -0,0 +1,63 @@ +/* Test STT_GNU_IFUNC resolver with second argument. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include + +static int +one (void) +{ + return 1; +} + +static uint64_t saved_arg1; +static __ifunc_arg_t saved_arg2; + +/* extern visible ifunc symbol. */ +int +foo (void); + +void * +foo_ifunc (uint64_t, const __ifunc_arg_t *) __asm__ ("foo"); +__asm__(".type foo, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_ifunc (uint64_t arg1, const __ifunc_arg_t *arg2) +{ + saved_arg1 = arg1; + if (arg1 & _IFUNC_ARG_HWCAP) + saved_arg2 = *arg2; + return (void *) one; +} + +static int +do_test (void) +{ + TEST_VERIFY (foo () == 1); + TEST_VERIFY (saved_arg1 & _IFUNC_ARG_HWCAP); + TEST_COMPARE ((uint32_t)saved_arg1, (uint32_t)getauxval (AT_HWCAP)); + TEST_COMPARE (saved_arg2._size, sizeof (__ifunc_arg_t)); + TEST_COMPARE (saved_arg2._hwcap, getauxval (AT_HWCAP)); + TEST_COMPARE (saved_arg2._hwcap2, getauxval (AT_HWCAP2)); + return 0; +} + +#include diff --git a/sysdeps/aarch64/tst-ifunc-arg-2.c b/sysdeps/aarch64/tst-ifunc-arg-2.c new file mode 100644 index 0000000000..9564818126 --- /dev/null +++ b/sysdeps/aarch64/tst-ifunc-arg-2.c @@ -0,0 +1,66 @@ +/* Test R_*_IRELATIVE resolver with second argument. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include + +static int +one (void) +{ + return 1; +} + +static uint64_t saved_arg1; +static __ifunc_arg_t saved_arg2; + +/* local ifunc symbol. */ +int +__attribute__ ((visibility ("hidden"))) +foo (void); + +static void * +__attribute__ ((used)) +foo_ifunc (uint64_t, const __ifunc_arg_t *) __asm__ ("foo"); +__asm__(".type foo, %gnu_indirect_function"); + +static void * +__attribute__ ((used)) +inhibit_stack_protector +foo_ifunc (uint64_t arg1, const __ifunc_arg_t *arg2) +{ + saved_arg1 = arg1; + if (arg1 & _IFUNC_ARG_HWCAP) + saved_arg2 = *arg2; + return (void *) one; +} + +static int +do_test (void) +{ + TEST_VERIFY (foo () == 1); + TEST_VERIFY (saved_arg1 & _IFUNC_ARG_HWCAP); + TEST_COMPARE ((uint32_t)saved_arg1, (uint32_t)getauxval (AT_HWCAP)); + TEST_COMPARE (saved_arg2._size, sizeof (__ifunc_arg_t)); + TEST_COMPARE (saved_arg2._hwcap, getauxval (AT_HWCAP)); + TEST_COMPARE (saved_arg2._hwcap2, getauxval (AT_HWCAP2)); + return 0; +} + +#include