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