Add test for elf-hash and packed relocations

This adds CTS test for system libraries to support
workaround for http://b/24465209: some apps require
a subset of system libs to have elf-hash and not to
use packed relocations.

Bug: http://b/32917341
Bug: http://b/24465209
Test: bionic-unit-tests --gtest_filter=Dl*:dl*
Change-Id: Ia0bc28506b1f1f97d4cf902d73b0769e2815fed3
diff --git a/libdl/Android.bp b/libdl/Android.bp
index e9b79d7..a0aeeff 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -21,6 +21,7 @@
     arch: {
         arm: {
             version_script: "libdl.arm.map",
+            ldflags: ["-Wl,--hash-style=both"],
         },
         arm64: {
             version_script: "libdl.arm64.map",
@@ -32,7 +33,10 @@
             version_script: "libdl.mips64.map",
         },
         x86: {
-            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+            ldflags: [
+                "-Wl,--exclude-libs=libgcc_eh.a",
+                "-Wl,--hash-style=both",
+            ],
             version_script: "libdl.x86.map",
         },
         x86_64: {
diff --git a/tests/Android.bp b/tests/Android.bp
index a9d302a..da90a92 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -273,7 +273,7 @@
 
 cc_test_library {
     name: "libBionicLoaderTests",
-    defaults: ["bionic_tests_defaults"],
+    defaults: ["bionic_tests_defaults", "llvm-defaults"],
     srcs: [
         "atexit_test.cpp",
         "dl_test.cpp",
@@ -298,6 +298,12 @@
             ],
             static_libs: [
                 "libpagemap",
+                "libLLVMObject",
+                "libLLVMBitReader",
+                "libLLVMMC",
+                "libLLVMMCParser",
+                "libLLVMCore",
+                "libLLVMSupport",
             ],
         }
     }
@@ -373,6 +379,12 @@
                 "libziparchive",
                 "libz",
                 "libutils",
+                "libLLVMObject",
+                "libLLVMBitReader",
+                "libLLVMMC",
+                "libLLVMMCParser",
+                "libLLVMCore",
+                "libLLVMSupport",
             ],
             ldflags: [
                 "-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs",
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 46f6ec0..1721019 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -31,6 +31,18 @@
 #include "dlfcn_symlink_support.h"
 #include "utils.h"
 
+#if defined(__BIONIC__) && (defined(__arm__) || defined(__i386__))
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/ADT/StringRef.h>
+#include <llvm/Object/Binary.h>
+#include <llvm/Object/ELFObjectFile.h>
+#include <llvm/Object/ObjectFile.h>
+
+#pragma clang diagnostic pop
+#endif //  defined(__ANDROID__) && (defined(__arm__) || defined(__i386__))
+
 #define ASSERT_SUBSTR(needle, haystack) \
     ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack)
 
@@ -1174,6 +1186,78 @@
 // Bionic specific tests
 #if defined(__BIONIC__)
 
+#if defined(__arm__) || defined(__i386__)
+const llvm::ELF::Elf32_Dyn* to_dynamic_table(const char* p) {
+  return reinterpret_cast<const llvm::ELF::Elf32_Dyn*>(p);
+}
+
+// Duplicate these definitions here because they are android specific
+// note that we cannot include <elf.h> because #defines conflict with
+// enum names provided by LLVM.
+#define DT_ANDROID_REL (llvm::ELF::DT_LOOS + 2)
+#define DT_ANDROID_RELA (llvm::ELF::DT_LOOS + 4)
+
+template<typename ELFT>
+void validate_compatibility_of_native_library(const std::string& path, ELFT* elf) {
+  bool has_elf_hash = false;
+  bool has_android_rel = false;
+  bool has_rel = false;
+  // Find dynamic section and check that DT_HASH and there is no DT_ANDROID_REL
+  for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
+    const llvm::object::ELFSectionRef& section_ref = *it;
+    if (section_ref.getType() == llvm::ELF::SHT_DYNAMIC) {
+      llvm::StringRef data;
+      ASSERT_TRUE(!it->getContents(data)) << "unable to get SHT_DYNAMIC section data";
+      for (auto d = to_dynamic_table(data.data()); d->d_tag != llvm::ELF::DT_NULL; ++d) {
+        if (d->d_tag == llvm::ELF::DT_HASH) {
+          has_elf_hash = true;
+        } else if (d->d_tag == DT_ANDROID_REL || d->d_tag == DT_ANDROID_RELA) {
+          has_android_rel = true;
+        } else if (d->d_tag == llvm::ELF::DT_REL || d->d_tag == llvm::ELF::DT_RELA) {
+          has_rel = true;
+        }
+      }
+
+      break;
+    }
+  }
+
+  ASSERT_TRUE(has_elf_hash) << path.c_str() << ": missing elf hash (DT_HASH)";
+  ASSERT_TRUE(!has_android_rel) << path.c_str() << ": has packed relocations";
+  ASSERT_TRUE(has_rel) << path.c_str() << ": missing DT_REL/DT_RELA";
+}
+
+void validate_compatibility_of_native_library(const char* soname) {
+  std::string path = std::string(PATH_TO_SYSTEM_LIB) + soname;
+  auto binary_or_error = llvm::object::createBinary(path);
+  ASSERT_FALSE(!binary_or_error);
+
+  llvm::object::Binary* binary = binary_or_error.get().getBinary();
+
+  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
+  ASSERT_TRUE(obj != nullptr);
+
+  auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj);
+
+  ASSERT_TRUE(elf != nullptr);
+
+  validate_compatibility_of_native_library(path, elf);
+}
+
+// This is a test for app compatibility workaround for arm and x86 apps
+// affected by http://b/24465209
+TEST(dlext, compat_elf_hash_and_relocation_tables) {
+  validate_compatibility_of_native_library("libc.so");
+  validate_compatibility_of_native_library("liblog.so");
+  validate_compatibility_of_native_library("libstdc++.so");
+  validate_compatibility_of_native_library("libdl.so");
+  validate_compatibility_of_native_library("libm.so");
+  validate_compatibility_of_native_library("libz.so");
+  validate_compatibility_of_native_library("libjnigraphics.so");
+}
+
+#endif //  defined(__arm__) || defined(__i386__)
+
 TEST(dlfcn, dt_runpath_absolute_path) {
   std::string libpath = get_testlib_root() + "/libtest_dt_runpath_d.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);