From 21975b2861d859fb580ddfba50d323740486b7bc Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Tue, 2 May 2017 16:31:56 -0700 Subject: [PATCH] Add ifunc for variable test-case Also move ifunc tests to clang and c++. Test: bionic-unit-tests --gtest_filter=dlfcn.ifunc* Change-Id: I008c4fc01470c8e4d314003c03a2b63ff9bf7fd8 --- tests/dlfcn_test.cpp | 48 +++++++++++--- tests/libs/Android.bp | 62 ++++++++++++++++++- ...stlib_ifunc.c => dlopen_testlib_ifunc.cpp} | 28 +++++---- tests/libs/dlopen_testlib_ifunc_variable.cpp | 24 +++++++ .../dlopen_testlib_ifunc_variable_impl.cpp | 53 ++++++++++++++++ 5 files changed, 193 insertions(+), 22 deletions(-) rename tests/libs/{dlopen_testlib_ifunc.c => dlopen_testlib_ifunc.cpp} (65%) create mode 100644 tests/libs/dlopen_testlib_ifunc_variable.cpp create mode 100644 tests/libs/dlopen_testlib_ifunc_variable_impl.cpp diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 4ff324e34..0ec46634c 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -247,6 +247,40 @@ TEST(dlfcn, dlopen_by_soname) { // mips doesn't support ifuncs #if !defined(__mips__) +TEST(dlfcn, ifunc_variable) { + typedef const char* (*fn_ptr)(); + + // ifunc's choice depends on whether IFUNC_CHOICE has a value + // first check the set case + setenv("IFUNC_CHOICE", "set", 1); + // preload libtest_ifunc_variable_impl.so + void* handle_impl = dlopen("libtest_ifunc_variable_impl.so", RTLD_NOW); + void* handle = dlopen("libtest_ifunc_variable.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + const char** foo_ptr = reinterpret_cast(dlsym(handle, "foo")); + fn_ptr foo_library_ptr = reinterpret_cast(dlsym(handle, "foo_library")); + ASSERT_TRUE(foo_ptr != nullptr) << dlerror(); + ASSERT_TRUE(foo_library_ptr != nullptr) << dlerror(); + ASSERT_EQ(strncmp("set", *foo_ptr, 3), 0); + ASSERT_EQ(strncmp("set", foo_library_ptr(), 3), 0); + dlclose(handle); + dlclose(handle_impl); + + // then check the unset case + unsetenv("IFUNC_CHOICE"); + handle_impl = dlopen("libtest_ifunc_variable_impl.so", RTLD_NOW); + handle = dlopen("libtest_ifunc_variable.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + foo_ptr = reinterpret_cast(dlsym(handle, "foo")); + foo_library_ptr = reinterpret_cast(dlsym(handle, "foo_library")); + ASSERT_TRUE(foo_ptr != nullptr) << dlerror(); + ASSERT_TRUE(foo_library_ptr != nullptr) << dlerror(); + ASSERT_EQ(strncmp("unset", *foo_ptr, 5), 0); + ASSERT_EQ(strncmp("unset", foo_library_ptr(), 5), 0); + dlclose(handle); + dlclose(handle_impl); +} + TEST(dlfcn, ifunc) { typedef const char* (*fn_ptr)(); @@ -254,11 +288,11 @@ TEST(dlfcn, ifunc) { // first check the set case setenv("IFUNC_CHOICE", "set", 1); void* handle = dlopen("libtest_ifunc.so", RTLD_NOW); - ASSERT_TRUE(handle != nullptr); + ASSERT_TRUE(handle != nullptr) << dlerror(); fn_ptr foo_ptr = reinterpret_cast(dlsym(handle, "foo")); fn_ptr foo_library_ptr = reinterpret_cast(dlsym(handle, "foo_library")); - ASSERT_TRUE(foo_ptr != nullptr); - ASSERT_TRUE(foo_library_ptr != nullptr); + ASSERT_TRUE(foo_ptr != nullptr) << dlerror(); + ASSERT_TRUE(foo_library_ptr != nullptr) << dlerror(); ASSERT_EQ(strncmp("set", foo_ptr(), 3), 0); ASSERT_EQ(strncmp("set", foo_library_ptr(), 3), 0); dlclose(handle); @@ -266,13 +300,13 @@ TEST(dlfcn, ifunc) { // then check the unset case unsetenv("IFUNC_CHOICE"); handle = dlopen("libtest_ifunc.so", RTLD_NOW); - ASSERT_TRUE(handle != nullptr); + ASSERT_TRUE(handle != nullptr) << dlerror(); foo_ptr = reinterpret_cast(dlsym(handle, "foo")); foo_library_ptr = reinterpret_cast(dlsym(handle, "foo_library")); - ASSERT_TRUE(foo_ptr != nullptr); - ASSERT_TRUE(foo_library_ptr != nullptr); + ASSERT_TRUE(foo_ptr != nullptr) << dlerror(); + ASSERT_TRUE(foo_library_ptr != nullptr) << dlerror(); ASSERT_EQ(strncmp("unset", foo_ptr(), 5), 0); - ASSERT_EQ(strncmp("unset", foo_library_ptr(), 3), 0); + ASSERT_EQ(strncmp("unset", foo_library_ptr(), 5), 0); dlclose(handle); } diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp index a031fe9ee..973a8d2f3 100644 --- a/tests/libs/Android.bp +++ b/tests/libs/Android.bp @@ -272,10 +272,8 @@ cc_test_library { cc_test_library { name: "libtest_ifunc", defaults: ["bionic_testlib_defaults"], - srcs: ["dlopen_testlib_ifunc.c"], + srcs: ["dlopen_testlib_ifunc.cpp"], - // TODO(dimitry): clang does not support ifunc attribute - clang: false, arch: { mips: { enabled: false, @@ -284,6 +282,64 @@ cc_test_library { enabled: false, }, }, + + target: { + android: { + shared_libs: ["libdl"], + }, + host: { + host_ldlibs: ["-ldl"], + }, + }, +} + +cc_test_library { + name: "libtest_ifunc_variable", + defaults: ["bionic_testlib_defaults"], + srcs: ["dlopen_testlib_ifunc_variable.cpp"], + shared_libs: [ "libtest_ifunc_variable_impl" ], + + arch: { + mips: { + enabled: false, + }, + mips64: { + enabled: false, + }, + }, + + target: { + android: { + shared_libs: ["libdl"], + }, + host: { + host_ldlibs: ["-ldl"], + }, + }, +} + +cc_test_library { + name: "libtest_ifunc_variable_impl", + defaults: ["bionic_testlib_defaults"], + srcs: ["dlopen_testlib_ifunc_variable_impl.cpp"], + + arch: { + mips: { + enabled: false, + }, + mips64: { + enabled: false, + }, + }, + + target: { + android: { + shared_libs: ["libdl"], + }, + host: { + host_ldlibs: ["-ldl"], + }, + }, } // ----------------------------------------------------------------------------- diff --git a/tests/libs/dlopen_testlib_ifunc.c b/tests/libs/dlopen_testlib_ifunc.cpp similarity index 65% rename from tests/libs/dlopen_testlib_ifunc.c rename to tests/libs/dlopen_testlib_ifunc.cpp index b68a3dd0f..f8acba741 100644 --- a/tests/libs/dlopen_testlib_ifunc.c +++ b/tests/libs/dlopen_testlib_ifunc.cpp @@ -14,52 +14,56 @@ * limitations under the License. */ +#include +#include #include #include -static int g_flag = 0; +static uintptr_t g_flag = 0; static void __attribute__((constructor)) init_flag() { - g_flag = 1; + g_flag = reinterpret_cast(dlsym(RTLD_DEFAULT, "dlsym")); } static const char* is_ctor_called() __attribute__ ((ifunc("is_ctor_called_ifun"))); -const char* foo() __attribute__ ((ifunc ("foo_ifunc"))); +extern "C" const char* foo() __attribute__ ((ifunc ("foo_ifunc"))); // Static linker creates GLOBAL/IFUNC symbol and JUMP_SLOT relocation type for plt segment -const char* is_ctor_called_jump_slot() __attribute__ ((ifunc("is_ctor_called_ifun"))); +extern "C" const char* is_ctor_called_jump_slot() __attribute__ ((ifunc("is_ctor_called_ifun"))); -const char* is_ctor_called_irelative() { +extern "C" const char* is_ctor_called_irelative() { // Call internal ifunc-resolved function with IRELATIVE reloc return is_ctor_called(); } -const char* return_true() { +extern "C" const char* return_true() { return "true"; } -const char* return_false() { +extern "C" const char* return_false() { return "false"; } -const char* f1() { +extern "C" const char* f1() { return "unset"; } -const char* f2() { +extern "C" const char* f2() { return "set"; } -void* is_ctor_called_ifun() { +typedef const char* (*fn_ptr)(); + +extern "C" fn_ptr is_ctor_called_ifun() { return g_flag == 0 ? return_false : return_true; } -void* foo_ifunc() { +extern "C" fn_ptr foo_ifunc() { char* choice = getenv("IFUNC_CHOICE"); return choice == NULL ? f1 : f2; } -const char* foo_library() { +extern "C" const char* foo_library() { return foo(); } diff --git a/tests/libs/dlopen_testlib_ifunc_variable.cpp b/tests/libs/dlopen_testlib_ifunc_variable.cpp new file mode 100644 index 000000000..a1f1ab6f1 --- /dev/null +++ b/tests/libs/dlopen_testlib_ifunc_variable.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +extern "C" const char* foo; + +extern "C" const char* foo_library() { + return foo; +} diff --git a/tests/libs/dlopen_testlib_ifunc_variable_impl.cpp b/tests/libs/dlopen_testlib_ifunc_variable_impl.cpp new file mode 100644 index 000000000..a550fefc3 --- /dev/null +++ b/tests/libs/dlopen_testlib_ifunc_variable_impl.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +static uintptr_t g_flag = 0; + +static void __attribute__((constructor)) init_flag() { + g_flag = reinterpret_cast(dlsym(RTLD_DEFAULT, "dlsym")); +} + +static const char* is_ctor_called() __attribute__ ((ifunc("is_ctor_called_ifun"))); + +extern "C" const char* foo() __attribute__ ((ifunc ("foo_ifunc"))); + +// Static linker creates GLOBAL/IFUNC symbol and JUMP_SLOT relocation type for plt segment +extern "C" const char* is_ctor_called_jump_slot() __attribute__ ((ifunc("is_ctor_called_ifun"))); + +extern "C" const char* is_ctor_called_irelative() { + // Call internal ifunc-resolved function with IRELATIVE reloc + return is_ctor_called(); +} + +extern "C" const char* var_true = "true"; +extern "C" const char* var_false = "false"; + +extern "C" const char* v1 = "unset"; +extern "C" const char* v2 = "set"; + +extern "C" void* is_ctor_called_ifun() { + return g_flag == 0 ? &var_false : &var_true; +} + +extern "C" void* foo_ifunc() { + char* choice = getenv("IFUNC_CHOICE"); + return choice == NULL ? &v1 : &v2; +}