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;
+}