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/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;