diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9c0018ea5c14..fbf7e59853b5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42030,6 +42030,10 @@ the stopped thread, as specified in @ref{thread-id syntax}.
 If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
 the core on which the stop event was detected.
 
+@item
+If @var{n} is @samp{tdesc}, then @var{r} is the hexadecimal number of
+the target description ID (@pxref{Target Description ID}).
+
 @item
 If @var{n} is a recognized @dfn{stop reason}, it describes a more
 specific event that stopped the target.  The currently defined stop
@@ -46546,7 +46550,7 @@ the following structure:
 @smallexample
 <?xml version="1.0"?>
 <threads>
-    <thread id="id" core="0" name="name">
+    <thread id="id" core="0" name="name" tdesc="0">
     ... description ...
     </thread>
 </threads>
@@ -46556,8 +46560,10 @@ Each @samp{thread} element must have the @samp{id} attribute that
 identifies the thread (@pxref{thread-id syntax}).  The
 @samp{core} attribute, if present, specifies which processor core
 the thread was last executing on.  The @samp{name} attribute, if
-present, specifies the human-readable name of the thread.  The content
-of the of @samp{thread} element is interpreted as human-readable
+present, specifies the human-readable name of the thread.  The
+@samp{tdesc} attribute, if present, specifies the target description
+ID of the thread (@pxref{Target Description ID}).  The content of
+the @samp{thread} element is interpreted as human-readable
 auxiliary information.  The @samp{handle} attribute, if present,
 is a hex encoded representation of the thread handle.
 
@@ -46767,6 +46773,8 @@ descriptions are accurate, and that @value{GDBN} understands them.
 target descriptions.  @xref{Expat}.
 
 @menu
+* Target Description ID::           Referencing different descriptions in the
+                                    remote protocol.
 * Retrieving Descriptions::         How descriptions are fetched from a target.
 * Target Description Format::       The contents of a target description.
 * Predefined Target Types::         Standard types available for target
@@ -46775,6 +46783,14 @@ target descriptions.  @xref{Expat}.
 * Standard Target Features::        Features @value{GDBN} knows about.
 @end menu
 
+@node Target Description ID
+@section Target Description ID
+
+In cases where a remote target supports threads having different
+target descriptions than their parent process, the remote protocol
+assigns a non-negative integer to each target description to reference
+it in the communication between the host and the target.
+
 @node Retrieving Descriptions
 @section Retrieving Descriptions
 
@@ -46787,6 +46803,11 @@ qXfer}).  The @var{annex} in the @samp{qXfer} packet will be
 XML document, of the form described in @ref{Target Description
 Format}.
 
+If target description IDs are being used (@pxref{Target Description ID}),
+@value{GDBN} can retrieve a target description with a given ID by using
+@samp{target-id-ID.xml} as the @var{annex}, where @var{ID} is the
+non-negative integer identifier of the desired target description.
+
 Alternatively, you can specify a file to read for the target description.
 If a file is set, the target will not be queried.  The commands to
 specify a file are:
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index 80310bc2c709..baff899307cc 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1049,6 +1049,53 @@ outreg (struct regcache *regcache, int regno, char *buf)
   return buf;
 }
 
+/* See remote-utils.h.  */
+
+unsigned int
+get_tdesc_rsp_id (const target_desc *tdesc)
+{
+  client_state &cs = get_client_state ();
+  unsigned int i;
+
+  for (i = 0; i < cs.tdescs.size (); i++)
+    if (cs.tdescs[i] == tdesc)
+      return i;
+
+  cs.tdescs.push_back (tdesc);
+
+  return i;
+}
+
+/* See remote-utils.h.  */
+
+const target_desc *
+get_tdesc_from_rsp_id (unsigned int id)
+{
+  client_state &cs = get_client_state ();
+
+  if (id >= cs.tdescs.size ())
+    return nullptr;
+
+  return cs.tdescs[id];
+}
+
+/* Return the ID as used in the remote protocol for the target descriptor of the
+   given PTID.  */
+
+static unsigned int
+get_tdesc_rsp_id (ptid_t ptid)
+{
+  const thread_info *thread = find_thread_ptid (ptid);
+  const target_desc *tdesc;
+
+  if (thread == nullptr)
+    tdesc = find_process_pid (ptid.pid ())->tdesc;
+  else
+    tdesc = get_thread_target_desc (thread);
+
+  return get_tdesc_rsp_id (tdesc);
+}
+
 void
 prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
 {
@@ -1241,6 +1288,16 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
 	    buf += strlen (buf);
 	    current_process ()->dlls_changed = false;
 	  }
+
+	if (current_thread->tdesc != nullptr
+	    && current_thread->tdesc != current_process ()->tdesc)
+	  {
+	    sprintf (buf, "tdesc:");
+	    buf += strlen (buf);
+	    sprintf (buf, "%x", get_tdesc_rsp_id (ptid));
+	    strcat (buf, ";");
+	    buf += strlen (buf);
+	  }
       }
       break;
     case TARGET_WAITKIND_EXITED:
diff --git a/gdbserver/remote-utils.h b/gdbserver/remote-utils.h
index cb2d6c346c99..61ef80b4dad7 100644
--- a/gdbserver/remote-utils.h
+++ b/gdbserver/remote-utils.h
@@ -75,4 +75,13 @@ int relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc);
 
 void monitor_output (const char *msg);
 
+/* Return the ID as used in the remote protocol for the given target
+   descriptor.  */
+
+unsigned int get_tdesc_rsp_id (const target_desc *tdesc);
+
+/* Return the target description corresponding to the remote protocol ID.  */
+
+const target_desc *get_tdesc_from_rsp_id (unsigned int id);
+
 #endif /* GDBSERVER_REMOTE_UTILS_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 21fb51a45d16..2d1062f98468 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -976,7 +976,15 @@ handle_general_set (char *own_buf)
 static const char *
 get_features_xml (const char *annex)
 {
-  const struct target_desc *desc = current_target_desc ();
+  const struct target_desc *desc;
+  unsigned int id;
+
+  if (strcmp (annex, "target.xml") == 0)
+    desc = current_target_desc ();
+  else if (sscanf (annex, "target-id-%u.xml", &id) == 1)
+    desc = get_tdesc_from_rsp_id (id);
+  else
+    desc = nullptr;
 
   /* `desc->xmltarget' defines what to return when looking for the
      "target.xml" file.  Its contents can either be verbatim XML code
@@ -986,7 +994,7 @@ get_features_xml (const char *annex)
      This variable is set up from the auto-generated
      init_registers_... routine for the current target.  */
 
-  if (strcmp (annex, "target.xml") == 0)
+  if (desc != nullptr)
     {
       const char *ret = tdesc_get_features_xml (desc);
 
@@ -1664,6 +1672,11 @@ handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
   if (name != NULL)
     buffer_xml_printf (buffer, " name=\"%s\"", name);
 
+  if (thread->tdesc != nullptr
+      && thread->tdesc != get_thread_process (thread)->tdesc)
+    buffer_xml_printf (buffer, " tdesc=\"%u\"",
+		       get_tdesc_rsp_id (thread->tdesc));
+
   if (handle_status)
     {
       char *handle_s = (char *) alloca (handle_len * 2 + 1);
diff --git a/gdbserver/server.h b/gdbserver/server.h
index 7997d1a32e6e..58be5027795b 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -193,6 +193,10 @@ struct client_state
   /* If true, memory tagging features are supported.  */
   bool memory_tagging_feature = false;
 
+  /* The target descriptions that have been communicated to the client.  The
+     index of a target description in this vector is the ID used to reference it
+     in the remote protocol.  */
+  std::vector<const target_desc *> tdescs;
 };
 
 client_state &get_client_state ();
