Merge "Add additional dl_phdr_info fields"
diff --git a/libc/bionic/dl_iterate_phdr_static.cpp b/libc/bionic/dl_iterate_phdr_static.cpp
index bbce1fc..7b44810 100644
--- a/libc/bionic/dl_iterate_phdr_static.cpp
+++ b/libc/bionic/dl_iterate_phdr_static.cpp
@@ -32,6 +32,10 @@
 #include <sys/types.h>
 #include <link.h>
 
+#include "private/bionic_elf_tls.h"
+#include "private/bionic_globals.h"
+#include "pthread_internal.h"
+
 /* ld provides this to us in the default link script */
 extern "C" void* __executable_start;
 
@@ -52,6 +56,21 @@
   exe_info.dlpi_name = NULL;
   exe_info.dlpi_phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<uintptr_t>(ehdr) + ehdr->e_phoff);
   exe_info.dlpi_phnum = ehdr->e_phnum;
+  exe_info.dlpi_adds = 0;
+  exe_info.dlpi_subs = 0;
+
+  const TlsModules& tls_modules = __libc_shared_globals()->tls_modules;
+  if (tls_modules.module_count == 0) {
+    exe_info.dlpi_tls_modid = 0;
+    exe_info.dlpi_tls_data = nullptr;
+  } else {
+    const size_t kExeModuleId = 1;
+    const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
+    const TlsModule& tls_module = tls_modules.module_table[__tls_module_id_to_idx(kExeModuleId)];
+    char* static_tls = reinterpret_cast<char*>(__get_bionic_tcb()) - layout.offset_bionic_tcb();
+    exe_info.dlpi_tls_modid = kExeModuleId;
+    exe_info.dlpi_tls_data = static_tls + tls_module.static_offset;
+  }
 
   // Try the executable first.
   int rc = cb(&exe_info, sizeof(exe_info), data);
@@ -71,6 +90,10 @@
   vdso_info.dlpi_name = NULL;
   vdso_info.dlpi_phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff);
   vdso_info.dlpi_phnum = ehdr_vdso->e_phnum;
+  vdso_info.dlpi_adds = 0;
+  vdso_info.dlpi_subs = 0;
+  vdso_info.dlpi_tls_modid = 0;
+  vdso_info.dlpi_tls_data = nullptr;
   for (size_t i = 0; i < vdso_info.dlpi_phnum; ++i) {
     if (vdso_info.dlpi_phdr[i].p_type == PT_LOAD) {
       vdso_info.dlpi_addr = (ElfW(Addr)) ehdr_vdso - vdso_info.dlpi_phdr[i].p_vaddr;
diff --git a/libc/include/link.h b/libc/include/link.h
index 072f093..bd430f5 100644
--- a/libc/include/link.h
+++ b/libc/include/link.h
@@ -47,6 +47,12 @@
   const char* dlpi_name;
   const ElfW(Phdr)* dlpi_phdr;
   ElfW(Half) dlpi_phnum;
+
+  // These fields were added in Android R.
+  unsigned long long dlpi_adds;
+  unsigned long long dlpi_subs;
+  size_t dlpi_tls_modid;
+  void* dlpi_tls_data;
 };
 
 #if defined(__arm__)
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;
diff --git a/tests/elftls_dl_test.cpp b/tests/elftls_dl_test.cpp
index 6d88880..012aad7 100644
--- a/tests/elftls_dl_test.cpp
+++ b/tests/elftls_dl_test.cpp
@@ -28,6 +28,7 @@
 
 #include <dlfcn.h>
 #include <gtest/gtest.h>
+#include <link.h>
 
 #include <thread>
 
@@ -335,3 +336,58 @@
   ASSERT_STREQ(nullptr, info.dli_sname);
   ASSERT_EQ(nullptr, info.dli_saddr);
 }
+
+TEST(elftls_dl, dl_iterate_phdr) {
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+
+  auto get_var_addr = reinterpret_cast<void*(*)()>(dlsym(lib, "get_large_tls_var_addr"));
+  ASSERT_NE(nullptr, get_var_addr);
+
+  struct TlsInfo {
+    bool found;
+    size_t modid;
+    void* data;
+    size_t memsz;
+  };
+
+  auto get_tls_info = []() {
+    auto callback = [](dl_phdr_info* info, size_t, void* data) {
+      TlsInfo& tls_info = *static_cast<TlsInfo*>(data);
+
+      // This test is also run with glibc, where dlpi_name may have relative path components, so
+      // examine just the basename when searching for the library.
+      if (strcmp(basename(info->dlpi_name), "libtest_elftls_dynamic.so") != 0) return 0;
+
+      tls_info.found = true;
+      tls_info.modid = info->dlpi_tls_modid;
+      tls_info.data = info->dlpi_tls_data;
+      for (ElfW(Half) i = 0; i < info->dlpi_phnum; ++i) {
+        if (info->dlpi_phdr[i].p_type == PT_TLS) {
+          tls_info.memsz = info->dlpi_phdr[i].p_memsz;
+        }
+      }
+      EXPECT_NE(static_cast<size_t>(0), tls_info.memsz);
+      return 1;
+    };
+
+    TlsInfo result {};
+    dl_iterate_phdr(callback, &result);
+    return result;
+  };
+
+  // The executable has a TLS segment, so it will use module ID #1, and the DSO's ID will be larger
+  // than 1. Initially, the data field is nullptr, because this thread's instance hasn't been
+  // allocated yet.
+  TlsInfo tls_info = get_tls_info();
+  ASSERT_TRUE(tls_info.found);
+  ASSERT_GT(tls_info.modid, static_cast<size_t>(1));
+  ASSERT_EQ(nullptr, tls_info.data);
+
+  void* var_addr = get_var_addr();
+
+  // Verify that dl_iterate_phdr returns a range of memory covering the allocated TLS variable.
+  tls_info = get_tls_info();
+  ASSERT_TRUE(tls_info.found);
+  ASSERT_GE(var_addr, tls_info.data);
+  ASSERT_LT(var_addr, static_cast<char*>(tls_info.data) + tls_info.memsz);
+}
diff --git a/tests/link_test.cpp b/tests/link_test.cpp
index cf5fc0b..75bb4d6 100644
--- a/tests/link_test.cpp
+++ b/tests/link_test.cpp
@@ -28,6 +28,7 @@
 
 #include <gtest/gtest.h>
 
+#include <dlfcn.h>
 #include <link.h>
 #if __has_include(<sys/auxv.h>)
 #include <sys/auxv.h>
@@ -80,6 +81,52 @@
   ASSERT_EQ(0, dl_iterate_phdr(Functor::Callback, &f));
 }
 
+// Verify that the module load/unload counters from dl_iterate_phdr are incremented.
+TEST(link, dl_iterate_phdr_counters) {
+  struct Counters {
+    bool inited = false;
+    uint64_t adds = 0;
+    uint64_t subs = 0;
+  };
+
+  auto get_adds_subs = []() {
+    auto callback = [](dl_phdr_info* info, size_t size, void* data) {
+      Counters& counters = *static_cast<Counters*>(data);
+      EXPECT_GE(size, sizeof(dl_phdr_info));
+      if (!counters.inited) {
+        counters.inited = true;
+        counters.adds = info->dlpi_adds;
+        counters.subs = info->dlpi_subs;
+      } else {
+        // The counters have the same value for each module.
+        EXPECT_EQ(counters.adds, info->dlpi_adds);
+        EXPECT_EQ(counters.subs, info->dlpi_subs);
+      }
+      return 0;
+    };
+
+    Counters counters {};
+    EXPECT_EQ(0, dl_iterate_phdr(callback, &counters));
+    EXPECT_TRUE(counters.inited);
+    return counters;
+  };
+
+  // dlopen increments the 'adds' counter.
+  const auto before_dlopen = get_adds_subs();
+  void* const handle = dlopen("libtest_empty.so", RTLD_NOW);
+  ASSERT_NE(nullptr, handle);
+  const auto after_dlopen = get_adds_subs();
+  ASSERT_LT(before_dlopen.adds, after_dlopen.adds);
+  ASSERT_EQ(before_dlopen.subs, after_dlopen.subs);
+
+  // dlclose increments the 'subs' counter.
+  const auto before_dlclose = after_dlopen;
+  dlclose(handle);
+  const auto after_dlclose = get_adds_subs();
+  ASSERT_EQ(before_dlclose.adds, after_dlclose.adds);
+  ASSERT_LT(before_dlclose.subs, after_dlclose.subs);
+}
+
 struct ProgHdr {
   const ElfW(Phdr)* table;
   size_t size;