diff --git a/gdb/arch/riscv.c b/gdb/arch/riscv.c
index a3ab8a92909..a02c18b4c91 100644
--- a/gdb/arch/riscv.c
+++ b/gdb/arch/riscv.c
@@ -25,37 +25,17 @@
 #include "../features/riscv/32bit-fpu.c"
 #include "../features/riscv/64bit-fpu.c"
 
-/* Wrapper used by std::unordered_map to generate hash for feature set.  */
-struct riscv_gdbarch_features_hasher
-{
-  std::size_t
-  operator() (const riscv_gdbarch_features &features) const noexcept
-  {
-    return features.hash ();
-  }
-};
-
-/* Cache of previously seen target descriptions, indexed by the feature set
-   that created them.  */
-static std::unordered_map<riscv_gdbarch_features,
-                          target_desc *,
-                          riscv_gdbarch_features_hasher> riscv_tdesc_cache;
+#ifndef GDBSERVER
+#define STATIC_IN_GDB static
+#else
+#define STATIC_IN_GDB
+#endif
 
 /* See arch/riscv.h.  */
 
-const target_desc *
-riscv_create_target_description (struct riscv_gdbarch_features features)
+STATIC_IN_GDB target_desc *
+riscv_create_target_description (const struct riscv_gdbarch_features features)
 {
-  /* Have we seen this feature set before?  If we have return the same
-     target description.  GDB expects that if two target descriptions are
-     the same (in content terms) then they will actually be the same
-     instance.  This is important when trying to lookup gdbarch objects as
-     GDBARCH_LIST_LOOKUP_BY_INFO performs a pointer comparison on target
-     descriptions to find candidate gdbarch objects.  */
-  const auto it = riscv_tdesc_cache.find (features);
-  if (it != riscv_tdesc_cache.end ())
-    return it->second;
-
   /* Now we should create a new target description.  */
   target_desc *tdesc = allocate_target_description ();
 
@@ -93,8 +73,43 @@ riscv_create_target_description (struct riscv_gdbarch_features features)
   else if (features.flen == 8)
     regnum = create_feature_riscv_64bit_fpu (tdesc, regnum);
 
+  return tdesc;
+}
+
+#ifndef GDBSERVER
+
+/* Wrapper used by std::unordered_map to generate hash for feature set.  */
+struct riscv_gdbarch_features_hasher
+{
+  std::size_t
+  operator() (const riscv_gdbarch_features &features) const noexcept
+  {
+    return features.hash ();
+  }
+};
+
+/* Cache of previously seen target descriptions, indexed by the feature set
+   that created them.  */
+static std::unordered_map<riscv_gdbarch_features,
+			  const target_desc *,
+			  riscv_gdbarch_features_hasher> riscv_tdesc_cache;
+
+/* See arch/riscv.h.  */
+
+const target_desc *
+riscv_lookup_target_description (const struct riscv_gdbarch_features features)
+{
+  /* Lookup in the cache.  */
+  const auto it = riscv_tdesc_cache.find (features);
+  if (it != riscv_tdesc_cache.end ())
+    return it->second;
+
+  target_desc *tdesc = riscv_create_target_description (features);
+
   /* Add to the cache.  */
   riscv_tdesc_cache.emplace (features, tdesc);
 
   return tdesc;
 }
+
+#endif /* !GDBSERVER */
diff --git a/gdb/arch/riscv.h b/gdb/arch/riscv.h
index 016a5fcd2f0..d40862cbd8e 100644
--- a/gdb/arch/riscv.h
+++ b/gdb/arch/riscv.h
@@ -66,10 +66,28 @@ struct riscv_gdbarch_features
   }
 };
 
-/* Create and return a target description that is compatible with
-   FEATURES.  */
+#ifdef GDBSERVER
+
+/* Create and return a target description that is compatible with FEATURES.
+   This is only used directly from the gdbserver where the created target
+   description is modified after it is return.  */
+
+target_desc *riscv_create_target_description
+	(const struct riscv_gdbarch_features features);
+
+#else
+
+/* Lookup an already existing target description matching FEATURES, or
+   create a new target description if this is the first time we have seen
+   FEATURES.  For the same FEATURES the same target_desc is always
+   returned.  This is important when trying to lookup gdbarch objects as
+   GDBARCH_LIST_LOOKUP_BY_INFO performs a pointer comparison on target
+   descriptions to find candidate gdbarch objects.  */
+
+const target_desc *riscv_lookup_target_description
+	(const struct riscv_gdbarch_features features);
+
+#endif /* GDBSERVER */
 
-const target_desc *riscv_create_target_description
-	(struct riscv_gdbarch_features features);
 
 #endif /* ARCH_RISCV_H */
diff --git a/gdb/nat/riscv-linux-tdesc.c b/gdb/nat/riscv-linux-tdesc.c
index 1b625cf38fc..3220725b875 100644
--- a/gdb/nat/riscv-linux-tdesc.c
+++ b/gdb/nat/riscv-linux-tdesc.c
@@ -31,10 +31,10 @@
 # define NFPREG 33
 #endif
 
-/* Determine XLEN and FLEN and return a corresponding target description.  */
+/* See nat/riscv-linux-tdesc.h.  */
 
-const struct target_desc *
-riscv_linux_read_description (int tid)
+struct riscv_gdbarch_features
+riscv_linux_read_features (int tid)
 {
   struct riscv_gdbarch_features features;
   elf_fpregset_t regs;
@@ -79,5 +79,5 @@ riscv_linux_read_description (int tid)
       break;
     }
 
-  return riscv_create_target_description (features);
+  return features;
 }
diff --git a/gdb/nat/riscv-linux-tdesc.h b/gdb/nat/riscv-linux-tdesc.h
index 9b57a9e99d5..e9ee64aa5d2 100644
--- a/gdb/nat/riscv-linux-tdesc.h
+++ b/gdb/nat/riscv-linux-tdesc.h
@@ -19,9 +19,10 @@
 #ifndef NAT_RISCV_LINUX_TDESC_H
 #define NAT_RISCV_LINUX_TDESC_H
 
-struct target_desc;
+#include "arch/riscv.h"
 
-/* Return a target description for the LWP identified by TID.  */
-const struct target_desc *riscv_linux_read_description (int tid);
+/* Determine XLEN and FLEN for the LWP identified by TID, and return a
+   corresponding features object.  */
+struct riscv_gdbarch_features riscv_linux_read_features (int tid);
 
 #endif /* NAT_RISCV_LINUX_TDESC_H */
diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c
index 2622f1b4399..c34ecd7cd5d 100644
--- a/gdb/riscv-linux-nat.c
+++ b/gdb/riscv-linux-nat.c
@@ -201,7 +201,9 @@ fill_fpregset (const struct regcache *regcache, prfpregset_t *fpregs,
 const struct target_desc *
 riscv_linux_nat_target::read_description ()
 {
-  return riscv_linux_read_description (inferior_ptid.lwp ());
+  const struct riscv_gdbarch_features features
+    = riscv_linux_read_feature (inferior_ptid.lwp ());
+  return riscv_lookup_target_description (features);
 }
 
 /* Fetch REGNUM (or all registers if REGNUM == -1) from the target
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index d585b0be5ab..bc816831941 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -2973,7 +2973,7 @@ riscv_find_default_target_description (const struct gdbarch_info info)
     features.xlen = 8;
 
   /* Now build a target description based on the feature set.  */
-  return riscv_create_target_description (features);
+  return riscv_lookup_target_description (features);
 }
 
 /* All of the registers in REG_SET are checked for in FEATURE, TDESC_DATA
