diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 09aa7be225e..9d418521839 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -1700,17 +1700,6 @@ dtpoff (struct bfd_link_info *info, bfd_vma address)
   return address - elf_hash_table (info)->tls_sec->vma - DTP_OFFSET;
 }
 
-/* Return the relocation value for a static TLS tp-relative relocation.  */
-
-static bfd_vma
-tpoff (struct bfd_link_info *info, bfd_vma address)
-{
-  /* If tls_sec is NULL, we should have signalled an error already.  */
-  if (elf_hash_table (info)->tls_sec == NULL)
-    return 0;
-  return address - elf_hash_table (info)->tls_sec->vma - TP_OFFSET;
-}
-
 /* Return the global pointer's value, or 0 if it is not in use.  */
 
 static bfd_vma
@@ -1725,6 +1714,25 @@ riscv_global_pointer_value (struct bfd_link_info *info)
   return h->u.def.value + sec_addr (h->u.def.section);
 }
 
+/* Return the relocation value for a static TLS tp-relative relocation.  */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+  /* If tls_sec is NULL, we try to use tp as gp, otherwise signal an error.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+  {
+    if (info->tp_as_gp & USED_TP_AS_GP)
+    {
+      bfd_vma gp = riscv_global_pointer_value (info);
+      bfd_vma tp = gp + 0x1000;
+      return address - tp;
+    }
+    return 0;
+  }
+  return address - elf_hash_table (info)->tls_sec->vma - TP_OFFSET;
+}
+
 /* Emplace a static relocation.  */
 
 static bfd_reloc_status_type
@@ -4657,6 +4665,28 @@ _bfd_riscv_relax_lui (bfd *abfd,
 	}
     }
 
+  unsigned sym = ELFNN_R_SYM (rel->r_info);
+  if ((link_info->tp_as_gp & ENABLE_TP_AS_GP)
+      && (elf_hash_table (link_info)->tls_sec == NULL))
+  {
+    bfd_vma tp = gp + 0x1000;
+    if (undefined_weak
+        || VALID_ITYPE_IMM (symval)
+        || (symval >= tp
+	    && VALID_ITYPE_IMM (symval - tp + max_alignment + reserve_size))
+        || (symval < tp
+	    && VALID_ITYPE_IMM (symval - tp - max_alignment - reserve_size)))
+    {
+      link_info->tp_as_gp |= USED_TP_AS_GP;
+      if (ELFNN_R_TYPE (rel->r_info) == R_RISCV_HI20)
+        rel->r_info = ELFNN_R_INFO (sym, R_RISCV_TPREL_HI20);
+      if (ELFNN_R_TYPE (rel->r_info) == R_RISCV_LO12_I)
+        rel->r_info = ELFNN_R_INFO (sym, R_RISCV_TPREL_LO12_I);
+      if (ELFNN_R_TYPE (rel->r_info) == R_RISCV_LO12_S)
+        rel->r_info = ELFNN_R_INFO (sym, R_RISCV_TPREL_LO12_S);
+    }
+  }
+
   /* Can we relax LUI to C.LUI?  Alignment might move the section forward;
      account for this assuming page alignment at worst. In the presence of 
      RELRO segment the linker aligns it by one page size, therefore sections
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 840790a298c..bf79035341d 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -557,6 +557,12 @@ struct bfd_link_info
   /* TRUE if commonpagesize is set on command-line.  */
   unsigned int commonpagesize_is_set : 1;
 
+  /* Tri-state variable:
+     0 => option --tp-as-gp is not specified by user.
+     1 => option --tp-as-gp is specified by user, but not used.
+     3 => option --tp-as-gp is specified by user, and has been used.  */
+  unsigned int tp_as_gp: 2;
+
   /* Char that may appear as the first char of a symbol, but should be
      skipped (like symbol_leading_char) when looking up symbols in
      wrap_hash.  Used by PowerPC Linux for 'dot' symbols.  */
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 0aa8b3359c4..5dfe7aee00d 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -115,6 +115,12 @@ END_RELOC_NUMBERS (R_RISCV_max)
 /* File uses the 32E base integer instruction.  */
 #define EF_RISCV_RVE 0x0008
 
+/* Enable the switch of option --tp-as-gp.  */
+#define ENABLE_TP_AS_GP 0x1
+
+/* Use the switch of option --tp-as-gp.  */
+#define USED_TP_AS_GP 0x2
+
 /* The name of the global pointer symbol.  */
 #define RISCV_GP_SYMBOL "__global_pointer$"
 
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 87cac02141d..ebd9e6282fb 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -54,6 +54,7 @@ enum option_values
   OPTION_OFORMAT,
   OPTION_RELAX,
   OPTION_NO_RELAX,
+  OPTION_RELAX_TP_AS_GP,
   OPTION_NO_SYMBOLIC,
   OPTION_RETAIN_SYMBOLS_FILE,
   OPTION_RPATH,
diff --git a/ld/ldmain.c b/ld/ldmain.c
index 06ac2c64fa8..d5c8fa62687 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -339,6 +339,7 @@ main (int argc, char **argv)
   link_info.combreloc = true;
   link_info.strip_discarded = true;
   link_info.prohibit_multiple_definition_absolute = false;
+  link_info.tp_as_gp = 0;
   link_info.textrel_check = DEFAULT_LD_TEXTREL_CHECK;
   link_info.emit_hash = DEFAULT_EMIT_SYSV_HASH;
   link_info.emit_gnu_hash = DEFAULT_EMIT_GNU_HASH;
diff --git a/ld/ldmain.h b/ld/ldmain.h
index dda124b96e8..f03ea59daea 100644
--- a/ld/ldmain.h
+++ b/ld/ldmain.h
@@ -56,6 +56,8 @@ extern char *error_handling_script;
   do { link_info.disable_target_specific_optimizations = 2; } while (0)
 #define ENABLE_RELAXATION		\
   do { link_info.disable_target_specific_optimizations = 0; } while (0)
+#define ENABLE_TP_AS_GP                 \
+  do { link_info.tp_as_gp = true; } while (0)
 
 extern void add_ysym (const char *);
 extern void add_wrap (const char *);
diff --git a/ld/lexsup.c b/ld/lexsup.c
index fe8722313fe..eaa126475ba 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -457,6 +457,8 @@ static const struct ld_option ld_options[] =
     '\0', NULL, N_("Reduce code size by using target specific optimizations"), TWO_DASHES },
   { {"no-relax", no_argument, NULL, OPTION_NO_RELAX},
     '\0', NULL, N_("Do not use relaxation techniques to reduce code size"), TWO_DASHES },
+  { {"tp-as-gp", no_argument, NULL, OPTION_RELAX_TP_AS_GP},
+    '\0', NULL, N_("Use tp as gp when there is no multithreaded scenario that requires the tp"), TWO_DASHES },
   { {"retain-symbols-file", required_argument, NULL,
      OPTION_RETAIN_SYMBOLS_FILE},
     '\0', N_("FILE"), N_("Keep only symbols listed in FILE"), TWO_DASHES },
@@ -1286,6 +1288,9 @@ parse_args (unsigned argc, char **argv)
 	case OPTION_RELAX:
 	  ENABLE_RELAXATION;
 	  break;
+        case OPTION_RELAX_TP_AS_GP:
+          ENABLE_TP_AS_GP;
+          break;
 	case OPTION_RETAIN_SYMBOLS_FILE:
 	  add_keepsyms_file (optarg);
 	  break;
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 947a266ba72..45a1d7e487e 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -124,6 +124,7 @@ if [istarget "riscv*-*-*"] {
     run_dump_test "pcgp-relax-01"
     run_dump_test "pcgp-relax-01-norelaxgp"
     run_dump_test "pcgp-relax-02"
+    run_dump_test "tp_as_gp"
     run_dump_test "c-lui"
     run_dump_test "c-lui-2"
     run_dump_test "disas-jalr"
diff --git a/ld/testsuite/ld-riscv-elf/tp_as_gp.d b/ld/testsuite/ld-riscv-elf/tp_as_gp.d
new file mode 100644
index 00000000000..51b2e141e63
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/tp_as_gp.d
@@ -0,0 +1,14 @@
+#source: tp_as_gp.s
+#as:
+#ld: --relax --tp-as-gp
+#objdump: -d
+
+
+.*:[ 	]+file format .*
+
+Disassembly of section \.text:
+
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+lui[ 	]+a5,.*
+.*:[ 	]+[0-9a-f]+[ 	]+add[ 	]+a5,a5,[0-9]+ # [0-9a-f]+ <global_array>
+.*:[ 	]+[0-9a-f]+[ 	]+add[ 	]+a5,tp,\-[0-9]+ # [0-9a-f]+ .*
\ No newline at end of file
diff --git a/ld/testsuite/ld-riscv-elf/tp_as_gp.s b/ld/testsuite/ld-riscv-elf/tp_as_gp.s
new file mode 100644
index 00000000000..6d723b6a14b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/tp_as_gp.s
@@ -0,0 +1,33 @@
+	.option nopic
+	.attribute arch, "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0"
+	.attribute unaligned_access, 0
+	.attribute stack_align, 16
+	.text
+
+	.globl	global_array
+	.bss
+	.align	3
+	.type	global_array, @object
+	.size	global_array, 5600
+global_array:
+	.zero	5600
+        
+	.globl	global_array2
+	.bss
+	.align	3
+	.type	global_array3, @object
+	.size	global_array3, 200
+global_array2:
+	.zero	200
+
+	.text
+	.align	1
+	.globl	_start
+	.type	_start, @function
+_start:
+	lui	a5,%hi(global_array)
+	addi	a5,a5,%lo(global_array)
+
+	lui	a5,%hi(global_array2)
+	addi	a5,a5,%lo(global_array2)
+
