Merge "Always process TLS relocs using general code path"
diff --git a/linker/linker_relocate.cpp b/linker/linker_relocate.cpp
index 72be5f7..c7c7bfb 100644
--- a/linker/linker_relocate.cpp
+++ b/linker/linker_relocate.cpp
@@ -229,6 +229,12 @@
   auto get_addend_norel = [&]() -> ElfW(Addr) { return 0; };
 #endif
 
+  if (!IsGeneral && __predict_false(is_tls_reloc(r_type))) {
+    // Always process TLS relocations using the slow code path, so that STB_LOCAL symbols are
+    // diagnosed, and ifunc processing is skipped.
+    return process_relocation_general(relocator, reloc);
+  }
+
   if (IsGeneral && is_tls_reloc(r_type)) {
     if (r_sym == 0) {
       // By convention in ld.bfd and lld, an omitted symbol on a TLS relocation
@@ -242,8 +248,15 @@
       //  - https://groups.google.com/d/topic/generic-abi/dJ4_Y78aQ2M/discussion
       //  - https://sourceware.org/bugzilla/show_bug.cgi?id=17699
       sym = &relocator.si_symtab[r_sym];
-      DL_ERR("unexpected TLS reference to local symbol \"%s\" in \"%s\": sym type %d, rel type %u",
-             sym_name, relocator.si->get_realpath(), ELF_ST_TYPE(sym->st_info), r_type);
+      auto sym_type = ELF_ST_TYPE(sym->st_info);
+      if (sym_type == STT_SECTION) {
+        DL_ERR("unexpected TLS reference to local section in \"%s\": sym type %d, rel type %u",
+               relocator.si->get_realpath(), sym_type, r_type);
+      } else {
+        DL_ERR(
+            "unexpected TLS reference to local symbol \"%s\" in \"%s\": sym type %d, rel type %u",
+            sym_name, relocator.si->get_realpath(), sym_type, r_type);
+      }
       return false;
     } else if (!lookup_symbol<IsGeneral>(relocator, r_sym, sym_name, &found_in, &sym)) {
       return false;
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