Add tests for dynamic ELF TLS
Bug: http://b/78026329
Test: bionic unit tests
Merged-In: I508fa38b331eeec7dae53039b4b1ec6cedea3034
Change-Id: I508fa38b331eeec7dae53039b4b1ec6cedea3034
diff --git a/tests/Android.bp b/tests/Android.bp
index e123f9e..b979f32 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -533,7 +533,6 @@
"libdl_preempt_test_1",
"libdl_preempt_test_2",
"libdl_test_df_1_global",
- "libelf-tls-library",
"libgnu-hash-table-library",
"libsysv-hash-table-library",
"libtestshared",
@@ -571,6 +570,10 @@
"libtest_dlsym_from_this",
"libtest_dlsym_weak_func",
"libtest_dt_runpath_d",
+ "libtest_elftls_dynamic",
+ "libtest_elftls_dynamic_filler_1",
+ "libtest_elftls_dynamic_filler_2",
+ "libtest_elftls_dynamic_filler_3",
"libtest_elftls_shared_var",
"libtest_elftls_shared_var_ie",
"libtest_elftls_tprel",
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index e3ba227..8a3b6f3 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1082,19 +1082,6 @@
ASSERT_SUBSTR("libsysv-hash-table-library.so", dlinfo.dli_fname);
}
-TEST(dlfcn, dlopen_library_with_ELF_TLS) {
-// TODO: Remove this test. Once ELF TLS is implemented, this test will be
-// replaced with a larger set of tests. Removing the test requires matching CLs
-// in CTS and in internal test suites.
-#if 0
- dlerror(); // Clear any pending errors.
- void* handle = dlopen("libelf-tls-library.so", RTLD_NOW);
- ASSERT_TRUE(handle == nullptr);
- ASSERT_SUBSTR("unknown reloc type ", dlerror());
-#endif
- GTEST_LOG_(INFO) << "This test is disabled pending replacement with dynamic ELF TLS tests.\n";
-}
-
TEST(dlfcn, dlopen_bad_flags) {
dlerror(); // Clear any pending errors.
void* handle;
diff --git a/tests/elftls_dl_test.cpp b/tests/elftls_dl_test.cpp
index 0a97c28..e908fb9 100644
--- a/tests/elftls_dl_test.cpp
+++ b/tests/elftls_dl_test.cpp
@@ -34,6 +34,10 @@
#include "gtest_globals.h"
#include "utils.h"
+#if defined(__BIONIC__)
+#include "bionic/pthread_internal.h"
+#endif
+
// Access libtest_elftls_shared_var.so's TLS variable using an IE access.
__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
@@ -78,3 +82,175 @@
eth.SetArgs({ helper.c_str(), nullptr });
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, error.c_str());
}
+
+// Use a GD access (__tls_get_addr or TLSDESC) to modify a variable in static
+// TLS memory.
+TEST(elftls_dl, access_static_tls) {
+ void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+ ASSERT_NE(nullptr, lib);
+
+ auto bump_shared_var = reinterpret_cast<int(*)()>(dlsym(lib, "bump_shared_var"));
+ ASSERT_NE(nullptr, bump_shared_var);
+
+ ASSERT_EQ(21, ++elftls_shared_var);
+ ASSERT_EQ(22, bump_shared_var());
+
+ std::thread([bump_shared_var] {
+ ASSERT_EQ(21, ++elftls_shared_var);
+ ASSERT_EQ(22, bump_shared_var());
+ }).join();
+}
+
+TEST(elftls_dl, bump_local_vars) {
+ void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+ ASSERT_NE(nullptr, lib);
+
+ auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
+ ASSERT_NE(nullptr, bump_local_vars);
+
+ ASSERT_EQ(42, bump_local_vars());
+ std::thread([bump_local_vars] {
+ ASSERT_EQ(42, bump_local_vars());
+ }).join();
+}
+
+// The behavior of accessing an unresolved weak TLS symbol using a dynamic TLS
+// relocation depends on which kind of implementation the target uses. With
+// TLSDESC, the result is NULL. With __tls_get_addr, the result is the
+// generation count (or maybe undefined behavior)? This test only tests TLSDESC.
+TEST(elftls_dl, missing_weak) {
+#if defined(__aarch64__)
+ void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+ ASSERT_NE(nullptr, lib);
+
+ auto missing_weak_dyn_tls_addr = reinterpret_cast<int*(*)()>(dlsym(lib, "missing_weak_dyn_tls_addr"));
+ ASSERT_NE(nullptr, missing_weak_dyn_tls_addr);
+
+ ASSERT_EQ(nullptr, missing_weak_dyn_tls_addr());
+ std::thread([missing_weak_dyn_tls_addr] {
+ ASSERT_EQ(nullptr, missing_weak_dyn_tls_addr());
+ }).join();
+#else
+ GTEST_LOG_(INFO) << "This test is only run on TLSDESC-based targets.\n";
+#endif
+}
+
+TEST(elftls_dl, dtv_resize) {
+#if defined(__BIONIC__)
+#define LOAD_LIB(soname) ({ \
+ auto lib = dlopen(soname, RTLD_LOCAL | RTLD_NOW); \
+ ASSERT_NE(nullptr, lib); \
+ reinterpret_cast<int(*)()>(dlsym(lib, "bump")); \
+ })
+
+ auto dtv = []() -> TlsDtv* { return __get_tcb_dtv(__get_bionic_tcb()); };
+
+ static_assert(sizeof(TlsDtv) == 3 * sizeof(void*),
+ "This test assumes that the Dtv has a 3-word header");
+
+ // Initially there are 3 modules:
+ // - the main test executable
+ // - libtest_elftls_shared_var
+ // - libtest_elftls_tprel
+
+ // The initial DTV is an empty DTV with no generation and a size of 0.
+ TlsDtv* zero_dtv = dtv();
+ ASSERT_EQ(0u, zero_dtv->count);
+ ASSERT_EQ(nullptr, zero_dtv->next);
+ ASSERT_EQ(kTlsGenerationNone, zero_dtv->generation);
+
+ // Load the fourth module.
+ auto func1 = LOAD_LIB("libtest_elftls_dynamic_filler_1.so");
+ ASSERT_EQ(101, func1());
+
+ // After loading one module, the DTV should be initialized to the next
+ // power-of-2 size (including the header).
+ TlsDtv* initial_dtv = dtv();
+ ASSERT_EQ(5u, initial_dtv->count);
+ ASSERT_EQ(zero_dtv, initial_dtv->next);
+ ASSERT_LT(0u, initial_dtv->generation);
+
+ // Load module 5.
+ auto func2 = LOAD_LIB("libtest_elftls_dynamic_filler_2.so");
+ ASSERT_EQ(102, func1());
+ ASSERT_EQ(201, func2());
+ ASSERT_EQ(initial_dtv, dtv());
+ ASSERT_EQ(5u, initial_dtv->count);
+
+ // Load module 6.
+ auto func3 = LOAD_LIB("libtest_elftls_dynamic_filler_3.so");
+ ASSERT_EQ(103, func1());
+ ASSERT_EQ(202, func2());
+
+#if defined(__aarch64__)
+ // The arm64 TLSDESC resolver doesn't update the DTV if it is new enough for
+ // the given access.
+ ASSERT_EQ(5u, dtv()->count);
+#else
+ // __tls_get_addr updates the DTV anytime the generation counter changes.
+ ASSERT_EQ(13u, dtv()->count);
+#endif
+
+ ASSERT_EQ(301, func3());
+
+ TlsDtv* new_dtv = dtv();
+ ASSERT_EQ(13u, new_dtv->count);
+ ASSERT_NE(initial_dtv, new_dtv);
+ ASSERT_EQ(initial_dtv, new_dtv->next);
+
+#undef LOAD_LIB
+#else
+ GTEST_LOG_(INFO) << "This test is skipped for glibc because it tests Bionic internals.";
+#endif
+}
+
+// Verify that variables are reset to their initial values after the library
+// containing them is closed.
+TEST(elftls_dl, dlclose_resets_values) {
+ for (int round = 0; round < 2; ++round) {
+ void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+ ASSERT_NE(nullptr, lib);
+
+ auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
+ ASSERT_NE(nullptr, bump_local_vars);
+
+ ASSERT_EQ(42, bump_local_vars());
+ ASSERT_EQ(44, bump_local_vars());
+
+ ASSERT_EQ(0, dlclose(lib));
+ }
+}
+
+// Calling dlclose should remove the entry for the solib from the global list of
+// ELF TLS modules. Test that repeatedly loading and unloading a library doesn't
+// increase the DTV size.
+TEST(elftls_dl, dlclose_removes_entry) {
+#if defined(__BIONIC__)
+ auto dtv = []() -> TlsDtv* { return __get_tcb_dtv(__get_bionic_tcb()); };
+
+ bool first = true;
+ size_t count = 0;
+
+ // Use a large number of rounds in case the DTV is initially larger than
+ // expected.
+ for (int round = 0; round < 32; ++round) {
+ void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+ ASSERT_NE(nullptr, lib);
+
+ auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
+ ASSERT_NE(nullptr, bump_local_vars);
+
+ ASSERT_EQ(42, bump_local_vars());
+ if (first) {
+ first = false;
+ count = dtv()->count;
+ } else {
+ ASSERT_EQ(count, dtv()->count);
+ }
+
+ dlclose(lib);
+ }
+#else
+ GTEST_LOG_(INFO) << "This test is skipped for glibc because it tests Bionic internals.";
+#endif
+}
diff --git a/tests/elftls_test.cpp b/tests/elftls_test.cpp
index 11d41ce..2d83d70 100644
--- a/tests/elftls_test.cpp
+++ b/tests/elftls_test.cpp
@@ -83,3 +83,17 @@
ASSERT_EQ(8, bump_static_tls_var_2());
}).join();
}
+
+// Because this C++ source file is built with -fpic, the compiler will access
+// this variable using a GD model. Typically, the static linker will relax the
+// GD to LE, but the arm32 linker doesn't do TLS relaxations, so we can test
+// calling __tls_get_addr in a static executable. The static linker knows that
+// the main executable's TlsIndex::module_id is 1 and writes that into the GOT.
+__thread int tlsvar_general = 30;
+
+TEST(elftls, general) {
+ ASSERT_EQ(31, ++tlsvar_general);
+ std::thread([] {
+ ASSERT_EQ(31, ++tlsvar_general);
+ }).join();
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 05d1ed2..d58b6b8 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -43,14 +43,6 @@
// Libraries and helper binaries for ELF TLS
// -----------------------------------------------------------------------------
cc_test_library {
- name: "libelf-tls-library",
- defaults: ["bionic_testlib_defaults"],
- srcs: ["elf_tls_test_library.cpp"],
- cflags: ["-fno-emulated-tls"],
- allow_undefined_symbols: true, // __tls_get_addr is undefined.
-}
-
-cc_test_library {
name: "libtest_elftls_shared_var",
defaults: ["bionic_testlib_defaults"],
srcs: ["elftls_shared_var.cpp"],
@@ -79,6 +71,35 @@
ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
}
+cc_test_library {
+ name: "libtest_elftls_dynamic",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_dynamic.cpp"],
+ cflags: ["-fno-emulated-tls"],
+ shared_libs: ["libtest_elftls_shared_var"],
+}
+
+cc_test_library {
+ name: "libtest_elftls_dynamic_filler_1",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_dynamic_filler.cpp"],
+ cflags: ["-fno-emulated-tls", "-DTLS_FILLER=100"],
+}
+
+cc_test_library {
+ name: "libtest_elftls_dynamic_filler_2",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_dynamic_filler.cpp"],
+ cflags: ["-fno-emulated-tls", "-DTLS_FILLER=200"],
+}
+
+cc_test_library {
+ name: "libtest_elftls_dynamic_filler_3",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_dynamic_filler.cpp"],
+ cflags: ["-fno-emulated-tls", "-DTLS_FILLER=300"],
+}
+
// -----------------------------------------------------------------------------
// Library to test gnu-styled hash
// -----------------------------------------------------------------------------
diff --git a/tests/libs/elf_tls_test_library.cpp b/tests/libs/elf_tls_test_library.cpp
deleted file mode 100644
index 56d0171..0000000
--- a/tests/libs/elf_tls_test_library.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-thread_local int elf_tls_variable;
-
-extern "C" int* get() { return &elf_tls_variable; }
diff --git a/tests/libs/elftls_dynamic.cpp b/tests/libs/elftls_dynamic.cpp
new file mode 100644
index 0000000..7fa239c
--- /dev/null
+++ b/tests/libs/elftls_dynamic.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+// This shared object test library is dlopen'ed by the main test executable.
+// This variable comes from libtest_elftls_shared_var.so, which is part of
+// static TLS. Verify that a GD-model access can access the variable.
+//
+// Accessing the static TLS variable from an solib prevents the static linker
+// from relaxing the GD access to IE and lets us test that __tls_get_addr and
+// the tlsdesc resolver handle a static TLS variable.
+extern "C" __thread int elftls_shared_var;
+
+extern "C" int bump_shared_var() {
+ return ++elftls_shared_var;
+}
+
+// The static linker denotes the current module by omitting the symbol from
+// the DTPMOD/TLSDESC relocations.
+static __thread int local_var_1 = 15;
+static __thread int local_var_2 = 25;
+
+extern "C" int bump_local_vars() {
+ return ++local_var_1 + ++local_var_2;
+}
+
+__attribute__((weak)) extern "C" __thread int missing_weak_dyn_tls;
+
+extern "C" int* missing_weak_dyn_tls_addr() {
+ return &missing_weak_dyn_tls;
+}
diff --git a/tests/libs/elftls_dynamic_filler.cpp b/tests/libs/elftls_dynamic_filler.cpp
new file mode 100644
index 0000000..9c00ab0
--- /dev/null
+++ b/tests/libs/elftls_dynamic_filler.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+__thread int var = TLS_FILLER;
+
+extern "C" int bump() {
+ return ++var;
+}