Fix dlclose for libraries with thread_local dtors

Introduce new flag to mark soinfo as TLS_NODELETE when
there are thread_local dtors associated with dso_handle
belonging to it.

Test: bionic-unit-tests --gtest_filter=dl*
Test: bionic-unit-tests-glibc --gtest_filter=dl*
Bug: https://github.com/android-ndk/ndk/issues/360
Change-Id: I724ef89fc899788f95c47e6372c38b3313f18fed
diff --git a/libc/Android.bp b/libc/Android.bp
index 20648d0..a0748a9 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1706,15 +1706,18 @@
     // Do not pack libc.so relocations; see http://b/20645321 for details.
     pack_relocations: false,
 
-    // WARNING: The only library libc.so should depend on is libdl.so!  If you add other libraries,
-    // make sure to add -Wl,--exclude-libs=libgcc.a to the LOCAL_LDFLAGS for those libraries.  This
-    // ensures that symbols that are pulled into those new libraries from libgcc.a are not declared
-    // external; if that were the case, then libc would not pull those symbols from libgcc.a as it
-    // should, instead relying on the external symbols from the dependent libraries.  That would
-    // create a "cloaked" dependency on libgcc.a in libc though the libraries, which is not what
-    // you wanted!
+    // WARNING: The only libraries libc.so should depend on are libdl.so and ld-android.so!
+    // If you add other libraries, make sure to add -Wl,--exclude-libs=libgcc.a to the
+    // LOCAL_LDFLAGS for those libraries.  This ensures that symbols that are pulled into
+    // those new libraries from libgcc.a are not declared external; if that were the case,
+    // then libc would not pull those symbols from libgcc.a as it should, instead relying
+    // on the external symbols from the dependent libraries.  That would create a "cloaked"
+    // dependency on libgcc.a in libc though the libraries, which is not what you wanted!
 
-    shared_libs: ["libdl"],
+    shared_libs: [
+        "ld-android",
+        "libdl",
+    ],
     whole_static_libs: ["libc_common", "libjemalloc"],
 
     nocrt: true,
diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp
index f687fd1..99077c1 100644
--- a/libc/bionic/__cxa_thread_atexit_impl.cpp
+++ b/libc/bionic/__cxa_thread_atexit_impl.cpp
@@ -28,6 +28,8 @@
 };
 
 extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle);
+extern "C" void __loader_add_thread_local_dtor(void* dso_handle) __attribute__((weak));
+extern "C" void __loader_remove_thread_local_dtor(void* dso_handle) __attribute__((weak));
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) {
@@ -40,6 +42,9 @@
   pthread_internal_t* thread = __get_thread();
   dtor->next = thread->thread_local_dtors;
   thread->thread_local_dtors = dtor;
+  if (__loader_add_thread_local_dtor != nullptr) {
+    __loader_add_thread_local_dtor(dso_handle);
+  }
   return 0;
 }
 
@@ -50,6 +55,9 @@
     thread->thread_local_dtors = current->next;
 
     current->func(current->arg);
+    if (__loader_remove_thread_local_dtor != nullptr) {
+      __loader_remove_thread_local_dtor(current->dso_handle);
+    }
     delete current;
   }
 }