riscv64 TLS support.

Signed-off-by: Mao Han <han_mao@linux.alibaba.com>
Signed-off-by: Xia Lifang <lifang_xia@linux.alibaba.com>
Signed-off-by: Chen Guoyin <chenguoyin.cgy@linux.alibaba.com>
Signed-off-by: Wang Chen <wangchen20@iscas.ac.cn>
Signed-off-by: Lu Xufan <luxufan@iscas.ac.cn>
Test: treehugger
Change-Id: I14efb4a03a3dc2ec736d7e47a3f8859c886eb9d6
diff --git a/libc/bionic/bionic_elf_tls.cpp b/libc/bionic/bionic_elf_tls.cpp
index d5fb05a..79893e3 100644
--- a/libc/bionic/bionic_elf_tls.cpp
+++ b/libc/bionic/bionic_elf_tls.cpp
@@ -137,6 +137,15 @@
   offset_bionic_tcb_ = reserve(sizeof(bionic_tcb), max_align);
   return offset_bionic_tcb_ - exe_size;
 
+#elif defined(__riscv)
+
+  // First reserve enough space for the TCB before the executable segment.
+  offset_bionic_tcb_ = reserve(sizeof(bionic_tcb), 1);
+
+  // Then reserve the segment itself.
+  const size_t exe_size = round_up_with_overflow_check(exe_segment->size, exe_segment->alignment);
+  return reserve(exe_size, 1);
+
 #else
 #error "Unrecognized architecture"
 #endif
@@ -312,7 +321,7 @@
     }
   }
 
-  return static_cast<char*>(mod_ptr) + ti->offset;
+  return static_cast<char*>(mod_ptr) + ti->offset + TLS_DTV_OFFSET;
 }
 
 // Returns the address of a thread's TLS memory given a module ID and an offset
@@ -332,7 +341,7 @@
   if (__predict_true(generation == dtv->generation)) {
     void* mod_ptr = dtv->modules[__tls_module_id_to_idx(ti->module_id)];
     if (__predict_true(mod_ptr != nullptr)) {
-      return static_cast<char*>(mod_ptr) + ti->offset;
+      return static_cast<char*>(mod_ptr) + ti->offset + TLS_DTV_OFFSET;
     }
   }
 
diff --git a/libc/platform/bionic/tls_defines.h b/libc/platform/bionic/tls_defines.h
index 78099b3..3e2efa3 100644
--- a/libc/platform/bionic/tls_defines.h
+++ b/libc/platform/bionic/tls_defines.h
@@ -114,6 +114,28 @@
 #define TLS_SLOT_BIONIC_TLS       9
 #define MAX_TLS_SLOT              9 // update this value when reserving a slot
 
+#elif defined(__riscv)
+
+// RISC-V ELF Specification[1] specifies that RISC-V uses Variant I as described
+// by the ELF TLS specification, with tp containing the address one past the end
+// of the TCB.
+//
+// [1]: RISC-V ELF Specification. Section: Thread Local Storage
+// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#thread-local-storage
+
+#define MIN_TLS_SLOT             (-9) // update this value when reserving a slot
+
+#define TLS_SLOT_BIONIC_TLS      (-9)
+#define TLS_SLOT_DTV             (-8)
+#define TLS_SLOT_THREAD_ID       (-7)
+#define TLS_SLOT_APP             (-6)
+#define TLS_SLOT_OPENGL          (-5)
+#define TLS_SLOT_OPENGL_API      (-4)
+#define TLS_SLOT_STACK_GUARD     (-3)
+#define TLS_SLOT_SANITIZER       (-2)
+#define TLS_SLOT_ART_THREAD_SELF (-1)
+#define MAX_TLS_SLOT             (-1)
+
 #endif
 
 #define BIONIC_TLS_SLOTS (MAX_TLS_SLOT - MIN_TLS_SLOT + 1)
diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h
index e0ec7b5..79ffcc4 100644
--- a/libc/private/bionic_elf_tls.h
+++ b/libc/private/bionic_elf_tls.h
@@ -201,3 +201,18 @@
 struct bionic_tcb;
 void __free_dynamic_tls(bionic_tcb* tcb);
 void __notify_thread_exit_callbacks();
+
+#if defined(__riscv)
+// TLS_DTV_OFFSET is a constant used in relocation fields, defined in RISC-V ELF Specification[1]
+// The front of the TCB contains a pointer to the DTV, and each pointer in DTV
+// points to 0x800 past the start of a TLS block to make full use of the range
+// of load/store instructions, refer to [2].
+//
+// [1]: RISC-V ELF Specification.
+// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#constants
+// [2]: Documentation of TLS data structures
+// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/53
+#define TLS_DTV_OFFSET 0x800
+#else
+#define TLS_DTV_OFFSET 0
+#endif
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d533ac3..9779c8d 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -371,7 +371,7 @@
     char* static_tls = reinterpret_cast<char*>(__get_bionic_tcb()) - layout.offset_bionic_tcb();
     return static_tls + tls_mod.static_offset;
   } else if (should_alloc) {
-    const TlsIndex ti { si_tls->module_id, 0 };
+    const TlsIndex ti { si_tls->module_id, static_cast<size_t>(0 - TLS_DTV_OFFSET) };
     return TLS_GET_ADDR(&ti);
   } else {
     TlsDtv* dtv = __get_tcb_dtv(__get_bionic_tcb());
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 60fd776..aa1a208 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -51,6 +51,8 @@
   return EM_AARCH64;
 #elif defined(__i386__)
   return EM_386;
+#elif defined(__riscv)
+  return EM_RISCV;
 #elif defined(__x86_64__)
   return EM_X86_64;
 #endif
diff --git a/linker/linker_relocate.cpp b/linker/linker_relocate.cpp
index c7c7bfb..952dade 100644
--- a/linker/linker_relocate.cpp
+++ b/linker/linker_relocate.cpp
@@ -49,7 +49,9 @@
     case R_GENERIC_TLS_DTPMOD:
     case R_GENERIC_TLS_DTPREL:
     case R_GENERIC_TLS_TPREL:
+#if defined(R_GENERIC_TLSDESC)
     case R_GENERIC_TLSDESC:
+#endif
       return true;
     default:
       return false;
@@ -426,7 +428,7 @@
     case R_GENERIC_TLS_DTPREL:
       count_relocation_if<IsGeneral>(kRelocRelative);
       {
-        const ElfW(Addr) result = sym_addr + get_addend_rel();
+        const ElfW(Addr) result = sym_addr + get_addend_rel() - TLS_DTV_OFFSET;
         trace_reloc("RELO TLS_DTPREL %16p <- %16p %s",
                     rel_target, reinterpret_cast<void*>(result), sym_name);
         *static_cast<ElfW(Addr)*>(rel_target) = result;
diff --git a/linker/linker_relocs.h b/linker/linker_relocs.h
index 93d899e..37a7880 100644
--- a/linker/linker_relocs.h
+++ b/linker/linker_relocs.h
@@ -73,6 +73,20 @@
 #define R_GENERIC_TLS_TPREL     R_386_TLS_TPOFF
 #define R_GENERIC_TLSDESC       R_386_TLS_DESC
 
+#elif defined (__riscv)
+
+#define R_GENERIC_JUMP_SLOT     R_RISCV_JUMP_SLOT
+#define R_GENERIC_ABSOLUTE      R_RISCV_64
+#define R_GENERIC_GLOB_DAT      R_RISCV_64
+#define R_GENERIC_RELATIVE      R_RISCV_RELATIVE
+#define R_GENERIC_IRELATIVE     R_RISCV_IRELATIVE
+#define R_GENERIC_COPY          R_RISCV_COPY
+#define R_GENERIC_TLS_DTPMOD    R_RISCV_TLS_DTPMOD64
+#define R_GENERIC_TLS_DTPREL    R_RISCV_TLS_DTPREL64
+#define R_GENERIC_TLS_TPREL     R_RISCV_TLS_TPREL64
+// TODO: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/94
+// #define R_GENERIC_TLSDESC       R_RISCV_TLS_DESC
+
 #elif defined (__x86_64__)
 
 #define R_GENERIC_JUMP_SLOT     R_X86_64_JUMP_SLOT