Add additional dl_phdr_info fields

Previously, Bionic's dl_phdr_info only included the first four
dl_iterate_phdr fields. Several other libc's have these additional fields:

    unsigned long long dlpi_adds -- incremented when a library is loaded
    unsigned long long dlpi_subs -- incremented when a library is unloaded
    size_t dlpi_tls_modid -- TLS module ID
    void* dlpi_tls_data -- pointer to current thread's TLS block or NULL

These extra fields are also exposed by glibc, musl, and FreeBSD. The
unwinder in libgcc.a, linked into shipping Android DSOs, has a
PC->eh_frame cache that activates if dl_phdr_info has the dlpi_adds and
dlpi_subs fields (indicated at run-time by a sufficiently-large size
argument to the callback).

Bug: https://github.com/android-ndk/ndk/issues/1062
Test: bionic unit tests
Change-Id: I6f0bab548cf8c828af2ddab9eb01c5c6d70cd81f
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d581dd8..f428490 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -46,8 +46,8 @@
 
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
-
 #include <async_safe/log.h>
+#include <bionic/pthread_internal.h>
 
 // Private C library headers.
 
@@ -86,6 +86,9 @@
 static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
 static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator;
 
+static uint64_t g_module_load_counter = 0;
+static uint64_t g_module_unload_counter = 0;
+
 static const char* const kLdConfigArchFilePath = "/system/etc/ld.config." ABI_STRING ".txt";
 
 static const char* const kLdConfigFilePath = "/system/etc/ld.config.txt";
@@ -424,6 +427,24 @@
   return true;
 }
 
+// Returns the address of the current thread's copy of a TLS module. If the current thread doesn't
+// have a copy yet, allocate one on-demand if should_alloc is true, and return nullptr otherwise.
+static inline void* get_tls_block_for_this_thread(const soinfo_tls* si_tls, bool should_alloc) {
+  const TlsModule& tls_mod = get_tls_module(si_tls->module_id);
+  if (tls_mod.static_offset != SIZE_MAX) {
+    const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
+    char* static_tls = reinterpret_cast<char*>(__get_bionic_tcb()) - layout.offset_bionic_tcb();
+    return static_tls + tls_mod.static_offset;
+  } else if (should_alloc) {
+    const TlsIndex ti { si_tls->module_id, 0 };
+    return TLS_GET_ADDR(&ti);
+  } else {
+    TlsDtv* dtv = __get_tcb_dtv(__get_bionic_tcb());
+    if (dtv->generation < tls_mod.first_generation) return nullptr;
+    return dtv->modules[__tls_module_id_to_idx(si_tls->module_id)];
+  }
+}
+
 #if defined(__arm__)
 
 // For a given PC, find the .so that it belongs to.
@@ -453,6 +474,16 @@
     dl_info.dlpi_name = si->link_map_head.l_name;
     dl_info.dlpi_phdr = si->phdr;
     dl_info.dlpi_phnum = si->phnum;
+    dl_info.dlpi_adds = g_module_load_counter;
+    dl_info.dlpi_subs = g_module_unload_counter;
+    if (soinfo_tls* tls_module = si->get_tls()) {
+      dl_info.dlpi_tls_modid = tls_module->module_id;
+      dl_info.dlpi_tls_data = get_tls_block_for_this_thread(tls_module, /*should_alloc=*/false);
+    } else {
+      dl_info.dlpi_tls_modid = 0;
+      dl_info.dlpi_tls_data = nullptr;
+    }
+
     rv = cb(&dl_info, sizeof(dl_phdr_info), data);
     if (rv != 0) {
       break;
@@ -2043,6 +2074,7 @@
            "... dlclose: unloading \"%s\"@%p ...",
            si->get_realpath(),
            si);
+    ++g_module_unload_counter;
     notify_gdb_of_unload(si);
     unregister_soinfo_tls(si);
     if (__libc_shared_globals()->unload_hook) {
@@ -2429,18 +2461,15 @@
     if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) {
       if (type == STT_TLS) {
         // For a TLS symbol, dlsym returns the address of the current thread's
-        // copy of the symbol. This function may allocate a DTV and/or storage
-        // for the source TLS module. (Allocating a DTV isn't necessary if the
-        // symbol is part of static TLS, but it's simpler to reuse
-        // __tls_get_addr.)
-        soinfo_tls* tls_module = found->get_tls();
+        // copy of the symbol.
+        const soinfo_tls* tls_module = found->get_tls();
         if (tls_module == nullptr) {
           DL_ERR("TLS symbol \"%s\" in solib \"%s\" with no TLS segment",
                  sym_name, found->get_realpath());
           return false;
         }
-        const TlsIndex ti { tls_module->module_id, sym->st_value };
-        *symbol = TLS_GET_ADDR(&ti);
+        void* tls_block = get_tls_block_for_this_thread(tls_module, /*should_alloc=*/true);
+        *symbol = static_cast<char*>(tls_block) + sym->st_value;
       } else {
         *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
       }
@@ -4106,6 +4135,7 @@
     }
   }
 
+  ++g_module_load_counter;
   notify_gdb_of_load(this);
   set_image_linked();
   return true;