Implement TLS_DTPMOD and TLS_DTPREL relocations

Generalize the omitted symbol and missing-TLS-segment behaviors to all TLS
relocations.

R_GENERIC_TLS_DTPMOD is a module ID, which starts at 1 for the executable.

R_GENERIC_TLS_DTPREL is an offset from the start of a module to a specific
TLS symbol.

binutils currently disagrees with Bionic about the values of
R_AARCH64_TLS_DTPMOD64 and R_AARCH64_TLS_DTPREL64, so disable
DTPMOD/DTPREL for now on arm64.

Bug: http://b/78026329
Test: bionic unit tests (Tests for dynamic TLS will be added later)
Change-Id: I05c28d6a1036bdd6127f605036679b7475689445
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 8a28bad..4dcdf7e 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2722,7 +2722,11 @@
     soinfo* lsi = nullptr;
 
     if (sym == 0) {
-      // Do nothing.
+      // By convention in ld.bfd and lld, an omitted symbol on a TLS relocation
+      // is a reference to the current module.
+      if (is_tls_reloc(type)) {
+        lsi = this;
+      }
     } else if (ELF_ST_BIND(symtab_[sym].st_info) == STB_LOCAL && is_tls_reloc(type)) {
       // In certain situations, the Gold linker accesses a TLS symbol using a
       // relocation to an STB_LOCAL symbol in .dynsym of either STT_SECTION or
@@ -2830,6 +2834,11 @@
                    sym_name, get_realpath());
             return false;
           }
+          if (lsi->get_tls() == nullptr) {
+            DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
+                   sym_name, lsi->get_realpath());
+            return false;
+          }
           sym_addr = s->st_value;
         } else {
           if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
@@ -2916,16 +2925,12 @@
         MARK(rel->r_offset);
         {
           ElfW(Addr) tpoff = 0;
-          if (sym == 0) {
-            // By convention in ld.bfd and lld, an omitted symbol
-            // (ELFW(R_SYM) == 0) refers to the local module.
-            lsi = this;
-          }
           if (lsi == nullptr) {
             // Unresolved weak relocation. Leave tpoff at 0 to resolve
             // &weak_tls_symbol to __get_tls().
-          } else if (soinfo_tls* lsi_tls = lsi->get_tls()) {
-            const TlsModule& mod = get_tls_module(lsi_tls->module_id);
+          } else {
+            CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+            const TlsModule& mod = get_tls_module(lsi->get_tls()->module_id);
             if (mod.static_offset != SIZE_MAX) {
               tpoff += mod.static_offset - tls_tp_base;
             } else {
@@ -2933,10 +2938,6 @@
                      sym_name, lsi->get_realpath(), get_realpath());
               return false;
             }
-          } else {
-            DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
-                   sym_name, lsi->get_realpath());
-            return false;
           }
           tpoff += sym_addr + addend;
           TRACE_TYPE(RELO, "RELO TLS_TPREL %16p <- %16p %s\n",
@@ -2946,6 +2947,35 @@
         }
         break;
 
+#if !defined(__aarch64__)
+      // Omit support for DTPMOD/DTPREL on arm64, at least until
+      // http://b/123385182 is fixed. arm64 uses TLSDESC instead.
+      case R_GENERIC_TLS_DTPMOD:
+        count_relocation(kRelocRelative);
+        MARK(rel->r_offset);
+        {
+          size_t module_id = 0;
+          if (lsi == nullptr) {
+            // Unresolved weak relocation. Evaluate the module ID to 0.
+          } else {
+            CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+            module_id = lsi->get_tls()->module_id;
+          }
+          TRACE_TYPE(RELO, "RELO TLS_DTPMOD %16p <- %zu %s\n",
+                     reinterpret_cast<void*>(reloc), module_id, sym_name);
+          *reinterpret_cast<ElfW(Addr)*>(reloc) = module_id;
+        }
+        break;
+      case R_GENERIC_TLS_DTPREL:
+        count_relocation(kRelocRelative);
+        MARK(rel->r_offset);
+        TRACE_TYPE(RELO, "RELO TLS_DTPREL %16p <- %16p %s\n",
+                   reinterpret_cast<void*>(reloc),
+                   reinterpret_cast<void*>(sym_addr + addend), sym_name);
+        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
+        break;
+#endif  // !defined(__aarch64__)
+
 #if defined(__aarch64__)
       case R_AARCH64_ABS64:
         count_relocation(kRelocAbsolute);