diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 6bd6e0ca29..54b8cd4331 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -1317,6 +1317,77 @@ TEST(dlfcn, dt_runpath_absolute_path) { dlclose(handle); } +TEST(dlfcn, dlclose_after_thread_local_dtor) { + bool is_dtor_triggered = false; + + auto f = [](void* handle, bool* is_dtor_triggered) { + typedef void (*fn_t)(bool*); + fn_t fn = reinterpret_cast(dlsym(handle, "init_thread_local_variable")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + + fn(is_dtor_triggered); + + ASSERT_TRUE(!*is_dtor_triggered); + }; + + void* handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); + + handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + std::thread t(f, handle, &is_dtor_triggered); + t.join(); + + ASSERT_TRUE(is_dtor_triggered); + dlclose(handle); + + handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); +} + +TEST(dlfcn, dlclose_before_thread_local_dtor) { + bool is_dtor_triggered = false; + + auto f = [](bool* is_dtor_triggered) { + void* handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); + + handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + typedef void (*fn_t)(bool*); + fn_t fn = reinterpret_cast(dlsym(handle, "init_thread_local_variable")); + ASSERT_TRUE(fn != nullptr) << dlerror(); + + fn(is_dtor_triggered); + + dlclose(handle); + + ASSERT_TRUE(!*is_dtor_triggered); + + // Since we have thread_atexit dtors associated with handle - the library should + // still be availabe. + handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle != nullptr) << dlerror(); + dlclose(handle); + }; + + void* handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + dlclose(handle); + + handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr); + + std::thread t(f, &is_dtor_triggered); + t.join(); + + ASSERT_TRUE(is_dtor_triggered); + handle = dlopen("libtest_thread_local_dtor.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle != nullptr) << dlerror(); +} + TEST(dlfcn, RTLD_macros) { #if !defined(RTLD_LOCAL) #error no RTLD_LOCAL diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp index e45eb6e646..61a837f91e 100644 --- a/tests/libs/Android.bp +++ b/tests/libs/Android.bp @@ -635,6 +635,16 @@ cc_test_library { shared_libs: ["libtest_dlopen_from_ctor"], } +// ----------------------------------------------------------------------------- +// Library with non-trivial thread_local variable to test dlclose() +// ----------------------------------------------------------------------------- +cc_test_library { + name: "libtest_thread_local_dtor", + defaults: ["bionic_testlib_defaults"], + srcs: ["thread_local_dtor.cpp"], +} + + // ----------------------------------------------------------------------------- // Tool to use to align the shared libraries in a zip file. // ----------------------------------------------------------------------------- diff --git a/tests/libs/thread_local_dtor.cpp b/tests/libs/thread_local_dtor.cpp new file mode 100644 index 0000000000..cefff7a37d --- /dev/null +++ b/tests/libs/thread_local_dtor.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 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. + */ + +namespace { + +class TestClass { + public: + TestClass(bool* flag) : flag_(flag) {} + ~TestClass() { + *flag_ = true; + } + private: + bool* flag_; +}; + +}; // namespace + +extern "C" void init_thread_local_variable(bool* flag) { + thread_local TestClass test(flag); +}