Fix dlsym and dladdr for TLS symbols

 * dlsym: call __tls_get_addr for TLS symbols

 * dladdr: skip TLS symbols

Bug: b/123772574
Test: bionic unit tests
Change-Id: I59a8bc4a7d455e1018b0d577b027b6417c8487cd
diff --git a/tests/elftls_dl_test.cpp b/tests/elftls_dl_test.cpp
index 88225d8..6d88880 100644
--- a/tests/elftls_dl_test.cpp
+++ b/tests/elftls_dl_test.cpp
@@ -269,3 +269,69 @@
   GTEST_SKIP() << "test doesn't apply to glibc";
 #endif
 }
+
+// Use dlsym to get the address of a TLS variable in static TLS and compare it
+// against the ordinary address of the variable.
+TEST(elftls_dl, dlsym_static_tls) {
+  void* lib = dlopen("libtest_elftls_shared_var.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+
+  int* var_addr = static_cast<int*>(dlsym(lib, "elftls_shared_var"));
+  ASSERT_EQ(&elftls_shared_var, var_addr);
+
+  std::thread([lib] {
+    int* var_addr = static_cast<int*>(dlsym(lib, "elftls_shared_var"));
+    ASSERT_EQ(&elftls_shared_var, var_addr);
+  }).join();
+}
+
+// Use dlsym to get the address of a TLS variable in dynamic TLS and compare it
+// against the ordinary address of the variable.
+TEST(elftls_dl, dlsym_dynamic_tls) {
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+  auto get_var_addr = reinterpret_cast<int*(*)()>(dlsym(lib, "get_large_tls_var_addr"));
+  ASSERT_NE(nullptr, get_var_addr);
+
+  int* var_addr = static_cast<int*>(dlsym(lib, "large_tls_var"));
+  ASSERT_EQ(get_var_addr(), var_addr);
+
+  std::thread([lib, get_var_addr] {
+    int* var_addr = static_cast<int*>(dlsym(lib, "large_tls_var"));
+    ASSERT_EQ(get_var_addr(), var_addr);
+  }).join();
+}
+
+// Calling dladdr on a TLS variable's address doesn't find anything.
+TEST(elftls_dl, dladdr_on_tls_var) {
+  Dl_info info;
+
+  // Static TLS variable
+  ASSERT_EQ(0, dladdr(&elftls_shared_var, &info));
+
+  // Dynamic TLS variable
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+  int* var_addr = static_cast<int*>(dlsym(lib, "large_tls_var"));
+  ASSERT_EQ(0, dladdr(var_addr, &info));
+}
+
+// Verify that dladdr does not misinterpret a TLS symbol's value as a virtual
+// address.
+TEST(elftls_dl, dladdr_skip_tls_symbol) {
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+
+  auto get_local_addr = reinterpret_cast<void*(*)()>(dlsym(lib, "get_local_addr"));
+  ASSERT_NE(nullptr, get_local_addr);
+  void* local_addr = get_local_addr();
+
+  Dl_info info;
+  ASSERT_NE(0, dladdr(local_addr, &info));
+
+  std::string libpath = GetTestlibRoot() + "/libtest_elftls_dynamic.so";
+  char dli_realpath[PATH_MAX];
+  ASSERT_TRUE(realpath(info.dli_fname, dli_realpath));
+  ASSERT_STREQ(libpath.c_str(), dli_realpath);
+  ASSERT_STREQ(nullptr, info.dli_sname);
+  ASSERT_EQ(nullptr, info.dli_saddr);
+}