diff --git a/libc/Android.bp b/libc/Android.bp index 8eefe10be..45c456903 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -1070,6 +1070,7 @@ cc_library_static { "bionic/atof.cpp", "bionic/bionic_allocator.cpp", "bionic/bionic_arc4random.cpp", + "bionic/bionic_call_ifunc_resolver.cpp", "bionic/bionic_futex.cpp", "bionic/bionic_netlink.cpp", "bionic/bionic_systrace.cpp", diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp new file mode 100644 index 000000000..85228359e --- /dev/null +++ b/libc/bionic/bionic_call_ifunc_resolver.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "private/bionic_call_ifunc_resolver.h" +#include +#include + +ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) { +#if defined(__aarch64__) + typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*); + static __ifunc_arg_t arg = { sizeof(__ifunc_arg_t), getauxval(AT_HWCAP), getauxval(AT_HWCAP2) }; + return reinterpret_cast(resolver_addr)(arg._hwcap | _IFUNC_ARG_HWCAP, &arg); +#elif defined(__arm__) + typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long); + static unsigned long hwcap = getauxval(AT_HWCAP); + return reinterpret_cast(resolver_addr)(hwcap); +#else + typedef ElfW(Addr) (*ifunc_resolver_t)(void); + return reinterpret_cast(resolver_addr)(); +#endif +} diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp index b4bddcef5..0b740232a 100644 --- a/libc/bionic/libc_init_static.cpp +++ b/libc/bionic/libc_init_static.cpp @@ -39,6 +39,7 @@ #include "libc_init_common.h" #include "pthread_internal.h" +#include "private/bionic_call_ifunc_resolver.h" #include "private/bionic_elf_tls.h" #include "private/bionic_globals.h" #include "private/bionic_macros.h" @@ -81,11 +82,10 @@ static void call_ifunc_resolvers() { return; } - typedef ElfW(Addr) (*ifunc_resolver_t)(void); for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) { ElfW(Addr)* offset = reinterpret_cast(r->r_offset); ElfW(Addr) resolver = r->r_addend; - *offset = reinterpret_cast(resolver)(); + *offset = __bionic_call_ifunc_resolver(resolver); } } #else @@ -103,11 +103,10 @@ static void call_ifunc_resolvers() { return; } - typedef ElfW(Addr) (*ifunc_resolver_t)(void); for (ElfW(Rel) *r = __rel_iplt_start; r != __rel_iplt_end; ++r) { ElfW(Addr)* offset = reinterpret_cast(r->r_offset); ElfW(Addr) resolver = *offset; - *offset = reinterpret_cast(resolver)(); + *offset = __bionic_call_ifunc_resolver(resolver); } } #endif diff --git a/libc/include/sys/ifunc.h b/libc/include/sys/ifunc.h new file mode 100644 index 000000000..7fbca4a4b --- /dev/null +++ b/libc/include/sys/ifunc.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#pragma once + +#include + +/** + * @file sys/ifunc.h + * @brief Declarations used for ifunc resolvers. Currently only meaningful for arm64. + */ + +__BEGIN_DECLS + +#if defined(__aarch64__) + +/** + * Provides information about hardware capabilities to ifunc resolvers. + * + * Starting with API level 30, ifunc resolvers on arm64 are passed two arguments. The first is a + * uint64_t whose value is equal to getauxval(AT_HWCAP) | _IFUNC_ARG_HWCAP. The second is a pointer + * to a data structure of this type. Prior to API level 30, no arguments are passed to ifunc + * resolvers. Code that wishes to be compatible with prior API levels should not accept any + * arguments in the resolver. + */ +typedef struct __ifunc_arg_t { + /** Set to sizeof(__ifunc_arg_t). */ + unsigned long _size; + + /** Set to getauxval(AT_HWCAP). */ + unsigned long _hwcap; + + /** Set to getauxval(AT_HWCAP2). */ + unsigned long _hwcap2; +} __ifunc_arg_t; + +/** + * If this bit is set in the first argument to an ifunc resolver, indicates that the second argument + * is a pointer to a data structure of type __ifunc_arg_t. This bit is always set on Android + * starting with API level 30. + */ +#define _IFUNC_ARG_HWCAP (1ULL << 62) + +#endif + +__END_DECLS diff --git a/libc/private/bionic_call_ifunc_resolver.h b/libc/private/bionic_call_ifunc_resolver.h new file mode 100644 index 000000000..e0ea35bba --- /dev/null +++ b/libc/private/bionic_call_ifunc_resolver.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +__LIBC_HIDDEN__ ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr); diff --git a/linker/linker.cpp b/linker/linker.cpp index 3c30e73b3..f4fccac1e 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -68,6 +68,7 @@ #include "linker_tls.h" #include "linker_utils.h" +#include "private/bionic_call_ifunc_resolver.h" #include "private/bionic_globals.h" #include "android-base/macros.h" #include "android-base/strings.h" @@ -2689,11 +2690,9 @@ bool link_namespaces_all_libs(android_namespace_t* namespace_from, ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { if (g_is_ldd) return 0; - typedef ElfW(Addr) (*ifunc_resolver_t)(void); - ifunc_resolver_t ifunc_resolver = reinterpret_cast(resolver_addr); - ElfW(Addr) ifunc_addr = ifunc_resolver(); + ElfW(Addr) ifunc_addr = __bionic_call_ifunc_resolver(resolver_addr); TRACE_TYPE(RELO, "Called ifunc_resolver@%p. The result is %p", - ifunc_resolver, reinterpret_cast(ifunc_addr)); + reinterpret_cast(resolver_addr), reinterpret_cast(ifunc_addr)); return ifunc_addr; } diff --git a/tests/ifunc_test.cpp b/tests/ifunc_test.cpp index 7ab589976..e69271f02 100644 --- a/tests/ifunc_test.cpp +++ b/tests/ifunc_test.cpp @@ -16,12 +16,19 @@ #include +#include +#if defined(__BIONIC__) +#include +#endif + +typedef int (*fn_ptr_t)(); + int ret42() { return 42; } -extern "C" void* resolver() { - return (void*)ret42; +extern "C" fn_ptr_t resolver() { + return ret42; } int ifunc() __attribute__((ifunc("resolver"))); @@ -29,3 +36,51 @@ int ifunc() __attribute__((ifunc("resolver"))); TEST(ifunc, function) { ASSERT_EQ(42, ifunc()); } + +#if defined(__BIONIC__) + +#if defined(__aarch64__) + +static uint64_t g_hwcap; +static __ifunc_arg_t g_arg; + +extern "C" fn_ptr_t hwcap_resolver(uint64_t hwcap, __ifunc_arg_t* arg) { + g_hwcap = hwcap; + g_arg = *arg; + return ret42; +} + +#elif defined(__arm__) + +static unsigned long g_hwcap; + +extern "C" fn_ptr_t hwcap_resolver(unsigned long hwcap) { + g_hwcap = hwcap; + return ret42; +} + +#else + +extern "C" fn_ptr_t hwcap_resolver() { + return ret42; +} + +#endif + +int hwcap() __attribute__((ifunc("hwcap_resolver"))); + +TEST(ifunc, hwcap) { + ASSERT_EQ(42, hwcap()); + +#if defined(__aarch64__) + EXPECT_EQ(getauxval(AT_HWCAP) | _IFUNC_ARG_HWCAP, g_hwcap); + + EXPECT_EQ(sizeof(__ifunc_arg_t), g_arg._size); + EXPECT_EQ(getauxval(AT_HWCAP), g_arg._hwcap); + EXPECT_EQ(getauxval(AT_HWCAP2), g_arg._hwcap2); +#elif defined(__arm__) + EXPECT_EQ(getauxval(AT_HWCAP), g_hwcap); +#endif +} + +#endif // defined(__BIONIC__)