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;