Always process TLS relocs using general code path

This is important for enabling the error about unsupported TLS
relocations to local symbols. The fast path tends to skip this error,
because it fails during lookup_symbol(). Add a test for this error.

I didn't see a performance regression in the linker_relocation
benchmark.

Bug: http://b/226978634
Test: m bionic-unit-tests
Change-Id: Ibef9bde2973cf8c2d420ecc9e8fe2c69a5097ce2
diff --git a/tests/Android.bp b/tests/Android.bp
index 3061142..48149c7 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -295,6 +295,29 @@
     },
 }
 
+cc_prebuilt_test_library_shared {
+    name: "libtest_invalid-local-tls",
+    strip: {
+        none: true,
+    },
+    check_elf_files: false,
+    relative_install_path: "bionic-loader-test-libs/prebuilt-elf-files",
+    arch: {
+        arm: {
+            srcs: ["prebuilt-elf-files/arm/libtest_invalid-local-tls.so"],
+        },
+        arm64: {
+            srcs: ["prebuilt-elf-files/arm64/libtest_invalid-local-tls.so"],
+        },
+        x86: {
+            srcs: ["prebuilt-elf-files/x86/libtest_invalid-local-tls.so"],
+        },
+        x86_64: {
+            srcs: ["prebuilt-elf-files/x86_64/libtest_invalid-local-tls.so"],
+        },
+    },
+}
+
 // -----------------------------------------------------------------------------
 // All standard tests.
 // -----------------------------------------------------------------------------
@@ -994,6 +1017,7 @@
         "libtest_init_fini_order_root",
         "libtest_init_fini_order_root2",
         "libtest_invalid-empty_shdr_table",
+        "libtest_invalid-local-tls",
         "libtest_invalid-rw_load_segment",
         "libtest_invalid-textrels",
         "libtest_invalid-textrels2",
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index e3664fd..940e726 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1654,6 +1654,21 @@
   ASSERT_SUBSTR(expected_dlerror.c_str(), dlerror());
 }
 
+TEST(dlfcn, dlopen_invalid_local_tls) {
+  const std::string libpath = GetPrebuiltElfDir() + "/libtest_invalid-local-tls.so";
+
+  void* handle = dlopen(libpath.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle == nullptr);
+#if defined(__arm__)
+  const char* referent = "local section";
+#else
+  const char* referent = "local symbol \"tls_var_2\"";
+#endif
+  std::string expected_dlerror = std::string("dlopen failed: unexpected TLS reference to ") +
+                                 referent + " in \"" + libpath + "\"";
+  ASSERT_SUBSTR(expected_dlerror.c_str(), dlerror());
+}
+
 TEST(dlfcn, dlopen_df_1_global) {
   void* handle = dlopen("libtest_dlopen_df_1_global.so", RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
diff --git a/tests/prebuilt-elf-files/arm/libtest_invalid-local-tls.so b/tests/prebuilt-elf-files/arm/libtest_invalid-local-tls.so
new file mode 100755
index 0000000..a42848d
--- /dev/null
+++ b/tests/prebuilt-elf-files/arm/libtest_invalid-local-tls.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/arm64/libtest_invalid-local-tls.so b/tests/prebuilt-elf-files/arm64/libtest_invalid-local-tls.so
new file mode 100755
index 0000000..20c5765
--- /dev/null
+++ b/tests/prebuilt-elf-files/arm64/libtest_invalid-local-tls.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/gen-libtest_invalid-local-tls.sh b/tests/prebuilt-elf-files/gen-libtest_invalid-local-tls.sh
new file mode 100755
index 0000000..0f3e736
--- /dev/null
+++ b/tests/prebuilt-elf-files/gen-libtest_invalid-local-tls.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Bionic doesn't support the references to STB_LOCAL symbols of type STT_TLS
+# and STT_SECTION that ld.gold generates. Set NDK21E to the path to a copy of
+# NDK r21e, which still has ld.gold (unlike the platform build or newer NDKs).
+
+set -e
+
+cat >test.c <<EOF
+  static __thread int tls_var_1;
+  extern __thread int tls_var_2;
+  int* getaddr1() { return &tls_var_1; }
+  int* getaddr2() { return &tls_var_2; }
+EOF
+cat >test2.c <<EOF
+  __attribute__((visibility("hidden"))) __thread int tls_var_2;
+EOF
+
+build() {
+  arch=$1
+  target=$2
+  $NDK21E/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -O2 --target=$target \
+      -fpic -shared -o $arch/libtest_invalid-local-tls.so -fno-emulated-tls \
+      -fuse-ld=gold test.c test2.c
+}
+
+build arm armv7a-linux-androideabi29
+build arm64 aarch64-linux-android29
+build x86 i686-linux-android29
+build x86_64 x86_64-linux-android29
diff --git a/tests/prebuilt-elf-files/x86/libtest_invalid-local-tls.so b/tests/prebuilt-elf-files/x86/libtest_invalid-local-tls.so
new file mode 100755
index 0000000..879f6a1
--- /dev/null
+++ b/tests/prebuilt-elf-files/x86/libtest_invalid-local-tls.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/x86_64/libtest_invalid-local-tls.so b/tests/prebuilt-elf-files/x86_64/libtest_invalid-local-tls.so
new file mode 100755
index 0000000..5b689ba
--- /dev/null
+++ b/tests/prebuilt-elf-files/x86_64/libtest_invalid-local-tls.so
Binary files differ