diff mbox series

[RFC,v4,09/15] GDB tdesc: Add vector type with number of elements given by math expression.

Message ID 20241102025635.586759-10-thiago.bauermann@linaro.org
State New
Headers show
Series gdbserver improvements for AArch64 SVE support | expand

Commit Message

Thiago Jung Bauermann Nov. 2, 2024, 2:56 a.m. UTC
This allows using MathML¹ to express the vector count, including
referencing the contents of other registers.  E.g.:

  <vector id="svevqu" type="uint128">
    <count id="svevq_count">
      <!-- How many 128-bit elements there are in a z register: $vg / 2  -->
      <math>
        <apply>
          <divide/>
          <ci>85</ci>
          <cn>2</cn>
        </apply>
      </math>
    </count>
  </vector>

The <ci> element (meaning "content identifier") is used to represent
variables.  In this case, it represents the VG register which has regnum
85 in the SVE AArch64 feature.  The <cn> element is used to represent
numbers.

MathML has two sets of markup.  One for representing a math expression
visually, and another to represent its meaning.  This patch uses the
latter markup, which is suited for computation.  It currently implements
just a subset of MathML to enable simple expressions.  If this approach
is accepted, we don't have to implement everything from the standard.
We could add things as we need them.

Currently, registers are referenced by their numbers but this is a
temporary hack.  I'll fix it to reference registers by their names
instead.

When the XML above is parsed, it's converted to a DWARF expression which
is stored in the GDB type.

When an XML feature contains a math expression referencing a register, it
needs to have an <architecture> tag.  This is because when generating the
C code corresponding to the XML, the feature XML is parsed in isolation
and not as part of a whole target description (which would contain the
architecture information).

One minor disadvantage of this approach is that gdbserver needs to have a
DWARF expression interpreter.  Since this feature only needs a subset of
the DWARF expression opcodes, it's a small amount of simple code and thus
it's not really a problem IMHO.

¹ https://www.w3.org/TR/MathML/
---
 gdb/features/gdb-target.dtd |  17 +-
 gdb/gdbarch-gen.c           |  32 +++
 gdb/gdbarch-gen.h           |   9 +
 gdb/gdbarch_components.py   |  11 +
 gdb/gdbtypes.c              |  61 ++++++
 gdb/gdbtypes.h              |   2 +
 gdb/target-descriptions.c   |  94 +++++++-
 gdb/target-descriptions.h   |   6 +
 gdb/xml-tdesc.c             | 416 +++++++++++++++++++++++++++++++++---
 gdbserver/Makefile.in       |   2 +
 gdbserver/config.in         |   3 +
 gdbserver/configure         |   6 +
 gdbserver/configure.ac      |   5 +
 gdbserver/locexpr.cc        | 107 ++++++++++
 gdbserver/locexpr.h         |  31 +++
 gdbserver/tdesc.cc          |  18 ++
 gdbsupport/tdesc.cc         | 134 +++++++++++-
 gdbsupport/tdesc.h          |  47 ++++
 18 files changed, 963 insertions(+), 38 deletions(-)
 create mode 100644 gdbserver/locexpr.cc
 create mode 100644 gdbserver/locexpr.h
diff mbox series

Patch

diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index d07703fca8b6..d38ca6f90bce 100644
--- a/gdb/features/gdb-target.dtd
+++ b/gdb/features/gdb-target.dtd
@@ -20,7 +20,7 @@ 
 <!ELEMENT compatible	(#PCDATA)>
 
 <!ELEMENT feature
-	((vector | flags | struct | union )*, reg*)>
+	(architecture?, (vector | flags | struct | union )*, reg*)>
 <!ATTLIST feature
 	name		ID	#REQUIRED>
 
@@ -34,11 +34,22 @@ 
 	group		CDATA	#IMPLIED
 	>
 
-<!ELEMENT vector	EMPTY>
+<!ELEMENT vector	(count?)>
 <!ATTLIST vector
 	id		CDATA	#REQUIRED
 	type		CDATA	#REQUIRED
-	count		CDATA	#REQUIRED>
+	count		CDATA	#IMPLIED>
+
+<!ELEMENT count		(math?)>
+<!ATTLIST count
+	id		ID	#IMPLIED
+	idref		IDREF	#IMPLIED>
+<!ELEMENT math		(apply | ci | cn)>
+<!ELEMENT apply		((divide | times), (ci | cn)+)>
+<!ELEMENT ci		(#PCDATA)>
+<!ELEMENT cn		(#PCDATA)>
+<!ELEMENT divide	EMPTY>
+<!ELEMENT times		EMPTY>
 
 <!ELEMENT flags		(field+)>
 <!ATTLIST flags
diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index 0d00cd7c9933..00afb37d7ef8 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -89,6 +89,7 @@  struct gdbarch
   gdbarch_ecoff_reg_to_regnum_ftype *ecoff_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_sdb_reg_to_regnum_ftype *sdb_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum = no_op_reg_to_regnum;
+  gdbarch_regnum_to_dwarf2_reg_ftype *regnum_to_dwarf2_reg = nullptr;
   gdbarch_register_name_ftype *register_name = nullptr;
   gdbarch_register_type_ftype *register_type = nullptr;
   gdbarch_dummy_id_ftype *dummy_id = default_dummy_id;
@@ -347,6 +348,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of ecoff_reg_to_regnum, invalid_p == 0.  */
   /* Skip verify of sdb_reg_to_regnum, invalid_p == 0.  */
   /* Skip verify of dwarf2_reg_to_regnum, invalid_p == 0.  */
+  /* Skip verify of regnum_to_dwarf2_reg, has predicate.  */
   if (gdbarch->register_name == 0)
     log.puts ("\n\tregister_name");
   if (gdbarch->register_type == 0)
@@ -711,6 +713,12 @@  gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: dwarf2_reg_to_regnum = <%s>\n",
 	      host_address_to_string (gdbarch->dwarf2_reg_to_regnum));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_regnum_to_dwarf2_reg_p() = %d\n",
+	      gdbarch_regnum_to_dwarf2_reg_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: regnum_to_dwarf2_reg = <%s>\n",
+	      host_address_to_string (gdbarch->regnum_to_dwarf2_reg));
   gdb_printf (file,
 	      "gdbarch_dump: register_name = <%s>\n",
 	      host_address_to_string (gdbarch->register_name));
@@ -2201,6 +2209,30 @@  set_gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch,
   gdbarch->dwarf2_reg_to_regnum = dwarf2_reg_to_regnum;
 }
 
+bool
+gdbarch_regnum_to_dwarf2_reg_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->regnum_to_dwarf2_reg != NULL;
+}
+
+int
+gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, int regnum)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->regnum_to_dwarf2_reg != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_regnum_to_dwarf2_reg called\n");
+  return gdbarch->regnum_to_dwarf2_reg (gdbarch, regnum);
+}
+
+void
+set_gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch,
+				  gdbarch_regnum_to_dwarf2_reg_ftype regnum_to_dwarf2_reg)
+{
+  gdbarch->regnum_to_dwarf2_reg = regnum_to_dwarf2_reg;
+}
+
 const char *
 gdbarch_register_name (struct gdbarch *gdbarch, int regnr)
 {
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index b982fd7cd092..1d4d19cf6774 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -308,6 +308,15 @@  typedef int (gdbarch_dwarf2_reg_to_regnum_ftype) (struct gdbarch *gdbarch, int d
 extern int gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int dwarf2_regnr);
 extern void set_gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum);
 
+/* Provide a default mapping from a gdb REGNUM to a DWARF2 register number.
+   Return -1 for bad REGNUM. */
+
+extern bool gdbarch_regnum_to_dwarf2_reg_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_regnum_to_dwarf2_reg_ftype) (struct gdbarch *gdbarch, int regnum);
+extern int gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, int regnum);
+extern void set_gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, gdbarch_regnum_to_dwarf2_reg_ftype *regnum_to_dwarf2_reg);
+
 /* Return the name of register REGNR for the specified architecture.
    REGNR can be any value greater than, or equal to zero, and less than
    'gdbarch_num_cooked_regs (GDBARCH)'.  If REGNR is not supported for
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 4006380076dc..0764a6c31d92 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -589,6 +589,17 @@  Return -1 for bad REGNUM.  Note: Several targets get this wrong.
     invalid=False,
 )
 
+Method(
+    comment="""
+Provide a default mapping from a gdb REGNUM to a DWARF2 register number.
+Return -1 for bad REGNUM.
+""",
+    type="int",
+    name="regnum_to_dwarf2_reg",
+    params=[("int", "regnum")],
+    predicate=True,
+)
+
 Method(
     comment="""
 Return the name of register REGNR for the specified architecture.
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index ea8bad4e826c..8037283e3706 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1030,6 +1030,40 @@  create_static_range_type (type_allocator &alloc, struct type *index_type,
   return result_type;
 }
 
+/* Create a range type using ALLOC.
+
+   Indices will be of type INDEX_TYPE, and will range from 0 to the value
+   LOC_EXPR_ evaluates to, inclusive.  */
+
+static struct type *
+create_dynamic_range_type (type_allocator &alloc, struct type *index_type,
+			   const gdb::array_view<const gdb_byte> locexpr_)
+{
+  struct dynamic_prop low, high;
+  struct dwarf2_property_baton *baton
+    = GDBARCH_OBSTACK_ZALLOC (alloc.arch (), dwarf2_property_baton);
+  size_t len = locexpr_.size () + 2;
+  gdb_byte *locexpr = GDBARCH_OBSTACK_CALLOC (alloc.arch (), len, gdb_byte);
+
+  memcpy (locexpr, locexpr_.data (), locexpr_.size());
+
+  /* The high bound is inclusive, so decrement the element count by one.  */
+  locexpr[len - 2] = DW_OP_lit1;
+  locexpr[len - 1] = DW_OP_minus;
+
+  baton->property_type = builtin_type (alloc.arch ())->builtin_unsigned_int;
+  baton->locexpr.data = locexpr;
+  baton->locexpr.size = len;
+  baton->locexpr.is_reference = false;
+  baton->locexpr.per_objfile = nullptr;
+  baton->locexpr.per_cu = nullptr;
+
+  low.set_const_val (0);
+  high.set_locexpr (baton);
+
+  return create_range_type (alloc, index_type, &low, &high, 0);
+}
+
 /* Predicate tests whether BOUNDS are static.  Returns 1 if all bounds values
    are static, otherwise returns 0.  */
 
@@ -1409,6 +1443,21 @@  lookup_array_range_type (struct type *element_type,
   return create_array_type (alloc, element_type, range_type);
 }
 
+/* Create type for array ranges where the low bound is 0 and the high
+   bound is given by the provided DWARF LOCEXPR_.  */
+
+static struct type *
+lookup_array_range_type (struct type *element_type,
+			 const gdb::array_view<const gdb_byte> locexpr_)
+{
+  type_allocator alloc (element_type);
+  struct type *index_type, *range_type;
+
+  index_type = builtin_type (element_type->arch ())->builtin_unsigned_int;
+  range_type = create_dynamic_range_type (alloc, index_type, locexpr_);
+  return create_array_type (alloc, element_type, range_type);
+}
+
 /* See gdbtypes.h.  */
 
 struct type *
@@ -1497,6 +1546,18 @@  init_vector_type (struct type *elt_type, int n)
   return array_type;
 }
 
+/* Create vector type of elements of type ELT_TYPE, where the number of
+   elements is given by the provided DWARF LOCEXPR.  */
+
+struct type *
+init_vector_type (struct type *elt_type,
+		  const gdb::array_view<const gdb_byte> locexpr)
+{
+  struct type *array_type = lookup_array_range_type (elt_type, locexpr);
+  make_vector_type (array_type);
+  return array_type;
+}
+
 /* Internal routine called by TYPE_SELF_TYPE to return the type that TYPE
    belongs to.  In c++ this is the class of "this", but TYPE_THIS_TYPE is too
    confusing.  "self" is a common enough replacement for "this".
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 514af300de3b..46103ea44656 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2427,6 +2427,8 @@  extern void append_flags_type_flag (struct type *type, int bitpos,
 
 extern void make_vector_type (struct type *array_type);
 extern struct type *init_vector_type (struct type *elt_type, int n);
+extern struct type *init_vector_type (struct type *elt_type,
+			       const gdb::array_view<const gdb_byte> locexpr);
 
 extern struct type *lookup_reference_type (struct type *, enum type_code);
 extern struct type *lookup_lvalue_reference_type (struct type *);
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 82e4d96276e3..9c9cc2ceadba 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -159,7 +159,10 @@  make_gdb_type (struct gdbarch *gdbarch, struct tdesc_type *ttype)
 	return;
 
       type *element_gdb_type = make_gdb_type (m_gdbarch, e->element_type);
-      m_type = init_vector_type (element_gdb_type, e->count);
+      if (e->locexpr.size() > 0)
+	m_type = init_vector_type (element_gdb_type, e->locexpr);
+      else
+	m_type = init_vector_type (element_gdb_type, e->count);
       m_type->set_name (xstrdup (e->name.c_str ()));
       return;
     }
@@ -364,6 +367,10 @@  struct target_desc : tdesc_element
   /* The features associated with this target.  */
   std::vector<tdesc_feature_up> features;
 
+  /* Location expressions for variable-size vector registers may use constants
+     defined in the DWARF header.  */
+  bool include_dwarf_header = false;
+
   /* Used to cache the generated xml version of the target description.  */
   mutable char *xmltarget = nullptr;
 
@@ -452,6 +459,23 @@  get_arch_data (struct gdbarch *gdbarch)
   return result;
 }
 
+/* See gdbsupport/tdesc.h.  */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+  struct gdbarch_info info;
+  info.bfd_arch_info = bfd_scan_arch (arch);
+  if (info.bfd_arch_info == nullptr)
+    return -1;
+
+  gdbarch *gdbarch = gdbarch_find_by_info (info);
+  if (gdbarch == nullptr)
+    return -1;
+
+  return gdbarch_dwarf2_reg_to_regnum (gdbarch, dwarf_reg);
+}
+
 /* The string manipulated by the "set tdesc filename ..." command.  */
 
 static std::string tdesc_filename_cmd_string;
@@ -1214,6 +1238,14 @@  set_tdesc_osabi (struct target_desc *target_desc, enum gdb_osabi osabi)
 {
   target_desc->osabi = osabi;
 }
+
+/* See gdb/target-descriptions.h.  */
+
+void
+set_tdesc_include_dwarf_header (struct target_desc *target_desc, bool include)
+{
+  target_desc->include_dwarf_header = include;
+}
 
 
 static struct cmd_list_element *tdesc_set_cmdlist, *tdesc_show_cmdlist;
@@ -1372,9 +1404,45 @@  class print_c_tdesc : public tdesc_element_visitor
     gdb_printf
       ("  element_type = tdesc_named_type (feature, \"%s\");\n",
        type->element_type->name.c_str ());
-    gdb_printf
-      ("  tdesc_create_vector (feature, \"%s\", element_type, %d);\n",
-       type->name.c_str (), type->count);
+
+    if (type->count == TDESC_REG_VARIABLE_SIZE)
+      {
+	const std::string *prefix;
+
+	if (type->locexpr_id.has_value ())
+	  prefix = &*type->locexpr_id;
+	else if (type->locexpr_idref.has_value ())
+	  prefix = &*type->locexpr_idref;
+	else
+	  prefix = &type->name;
+
+	if (!type->locexpr_idref.has_value ())
+	  {
+	    gdb_printf ("  gdb_byte %s_locexpr[%zu];\n", prefix->c_str (),
+			type->locexpr.size ());
+	    for (unsigned int i = 0; i < type->locexpr.size (); i++)
+	      {
+		const char *op_name;
+
+		if (type->locexpr_str.has_value ())
+		  op_name = type->locexpr_str->at (i);
+		else
+		  op_name = nullptr;
+
+		if (op_name == nullptr)
+		  gdb_printf ("  %s_locexpr[%u] = %u;\n", prefix->c_str (), i,
+			      type->locexpr[i]);
+		else
+		  gdb_printf ("  %s_locexpr[%u] = %s;\n", prefix->c_str (), i,
+			      op_name);
+	      }
+	  }
+	gdb_printf ("  tdesc_create_vector (feature, \"%s\", element_type, %s_locexpr);\n",
+		    type->name.c_str (), prefix->c_str ());
+      }
+    else
+      gdb_printf ("  tdesc_create_vector (feature, \"%s\", element_type, %d);\n",
+		  type->name.c_str (), type->count);
 
     gdb_printf ("\n");
   }
@@ -1494,7 +1562,13 @@  class print_c_tdesc : public tdesc_element_visitor
       gdb_printf ("\"%s\", ", reg->group.c_str ());
     else
       gdb_printf ("NULL, ");
-    gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+    if (reg->bitsize == TDESC_REG_VARIABLE_SIZE)
+      gdb_printf ("TDESC_REG_VARIABLE_SIZE, ");
+    else
+      gdb_printf ("%d, ", reg->bitsize);
+
+    gdb_printf ("\"%s\"", reg->type.c_str ());
 
     if (reg->load_early)
       gdb_printf (", true");
@@ -1558,6 +1632,8 @@  class print_c_feature : public print_c_tdesc
 		lbasename (m_filename_after_features.c_str ()));
 
     gdb_printf ("#include \"gdbsupport/tdesc.h\"\n");
+    if (e->include_dwarf_header)
+      gdb_printf ("#include \"dwarf2.h\"\n");
     gdb_printf ("\n");
   }
 
@@ -1642,7 +1718,13 @@  class print_c_feature : public print_c_tdesc
       gdb_printf ("\"%s\", ", reg->group.c_str ());
     else
       gdb_printf ("NULL, ");
-    gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+    if (reg->bitsize == TDESC_REG_VARIABLE_SIZE)
+      gdb_printf ("TDESC_REG_VARIABLE_SIZE, ");
+    else
+      gdb_printf ("%d, ", reg->bitsize);
+
+    gdb_printf ("\"%s\"", reg->type.c_str ());
 
     if (reg->load_early)
       gdb_printf (", true");
diff --git a/gdb/target-descriptions.h b/gdb/target-descriptions.h
index e40c7db5f79f..c65e5d203933 100644
--- a/gdb/target-descriptions.h
+++ b/gdb/target-descriptions.h
@@ -229,6 +229,12 @@  void set_tdesc_property (struct target_desc *,
 void tdesc_add_compatible (struct target_desc *,
 			   const struct bfd_arch_info *);
 
+/* Set whether the C version of the target description should include
+   dwarf2.h.  */
+
+void set_tdesc_include_dwarf_header (struct target_desc *target_desc,
+				     bool include);
+
 #if GDB_SELF_TEST
 namespace selftests {
 
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index 436c493d4f91..2f28968838d6 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -71,21 +71,62 @@  static std::unordered_map<std::string, target_desc_up> xml_cache;
 struct tdesc_parsing_data
 {
   /* The target description we are building.  */
-  struct target_desc *tdesc;
+  struct target_desc *tdesc = nullptr;
 
   /* The target feature we are currently parsing, or last parsed.  */
-  struct tdesc_feature *current_feature;
+  struct tdesc_feature *current_feature = nullptr;
 
   /* The register number to use for the next register we see, if
      it does not have its own.  This starts at zero.  */
-  int next_regnum;
+  int next_regnum = 0;
 
   /* The struct or union we are currently parsing, or last parsed.  */
-  tdesc_type_with_fields *current_type;
+  tdesc_type_with_fields *current_type = nullptr;
 
   /* The byte size of the current struct/flags type, if specified.  Zero
      if not specified.  Flags values must specify a size.  */
-  int current_type_size;
+  int current_type_size = 0;
+
+  /* Map used to implement id/idref attribute support for the <count>
+     element in a vector type.  The fist member is the count id, and the
+     second is the DWARF expression it corresponds to.  */
+  std::unordered_map<std::string, gdb::byte_vector> vector_count_locexprs;
+
+  /* Information needed to parse <vector> element.  */
+  struct {
+    /* id attribute for the <vector> element being processed.  */
+    std::string id;
+
+    /* Type of field in <vector> element being processed.  */
+    struct tdesc_type *field_type = nullptr;
+
+    /* Holds number of elements, if <vector> has "count" attribute.  */
+    std::optional<int> count;
+
+    /* Holds DWARF expression, if <vector> has a <math> child.  */
+    gdb::byte_vector locexpr;
+
+    /* Holds string representation of opcodes in locexpr.  Used to provide
+       more readable code when converting to C.  Needs to have the same
+       number of elements as locexpr.  */
+    std::optional<std::vector<const char *>> locexpr_str;
+
+    /* id attribute for the <count> element being processed.  */
+    std::string count_id;
+
+    std::string count_idref;
+
+    std::optional<gdb_byte> apply_operator;
+
+    std::optional<const char *> apply_operator_str;
+  } current_vector;
+
+  /* gdbarch that roughly corresponds to the XML target description being
+     parsed.  Used to gather basic facts about the architecture such as the
+     DWARF register numbering.  */
+  gdbarch *preliminary_gdbarch = nullptr;
+
+  std::set<int> load_early_regnums;
 };
 
 /* Handle the end of an <architecture> element and its value.  */
@@ -166,6 +207,36 @@  tdesc_start_feature (struct gdb_xml_parser *parser,
   data->current_feature = tdesc_create_feature (data->tdesc, name);
 }
 
+/* Handle the end of a <feature> element.  */
+
+static void
+tdesc_end_feature (struct gdb_xml_parser *parser,
+		   const struct gdb_xml_element *element, void *user_data,
+		   const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  for (int regnum : data->load_early_regnums)
+    {
+      std::vector<tdesc_reg_up> &registers = data->current_feature->registers;
+      auto reg = std::find_if (registers.begin (), registers.end (),
+			       [=] (tdesc_reg_up &i) {
+				 if (i->target_regnum == regnum)
+				   return true;
+				 else
+				   return false;
+			       });
+      if (reg == registers.end ())
+	gdb_xml_error (parser,
+		       _("Register number %d used in expression but not found in feature."),
+		       regnum);
+
+      (*reg)->load_early = true;
+    }
+
+  data->load_early_regnums.clear ();
+}
+
 /* Handle the start of a <reg> element.  Fill in the optional
    attributes and attach it to the containing feature.  */
 
@@ -474,27 +545,287 @@  tdesc_start_vector (struct gdb_xml_parser *parser,
 		    void *user_data, std::vector<gdb_xml_value> &attributes)
 {
   struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
-  struct tdesc_type *field_type;
-  char *id, *field_type_id;
-  ULONGEST count;
+  char *field_type_id;
 
-  id = (char *) attributes[0].value.get ();
+  data->current_vector.id = (char *) attributes[0].value.get ();
   field_type_id = (char *) attributes[1].value.get ();
-  count = * (ULONGEST *) attributes[2].value.get ();
 
-  if (count > MAX_VECTOR_SIZE)
+  if (attributes.size () >= 3)
     {
-      gdb_xml_error (parser,
-		     _("Vector size %s is larger than maximum (%d)"),
-		     pulongest (count), MAX_VECTOR_SIZE);
+      ULONGEST count = *(ULONGEST *)attributes[2].value.get ();
+
+      if (count > MAX_VECTOR_SIZE)
+	gdb_xml_error (parser,
+		       _ ("Vector \"%s\": size %s is larger than maximum (%d)"),
+		       data->current_vector.id.c_str (), pulongest (count),
+		       MAX_VECTOR_SIZE);
+
+      data->current_vector.count = (int) count;
     }
 
-  field_type = tdesc_named_type (data->current_feature, field_type_id);
-  if (field_type == NULL)
+  data->current_vector.field_type = tdesc_named_type (data->current_feature,
+						      field_type_id);
+  if (data->current_vector.field_type == nullptr)
     gdb_xml_error (parser, _("Vector \"%s\" references undefined type \"%s\""),
-		   id, field_type_id);
+		   data->current_vector.id.c_str (), field_type_id);
+}
+
+/* Handle the end of a <vector> element.  */
+
+static void
+tdesc_end_vector (struct gdb_xml_parser *parser,
+		  const struct gdb_xml_element *element, void *user_data,
+		  const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (data->current_vector.count.has_value ()
+      && !data->current_vector.locexpr.empty ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\" has both a count attribute and a count expression"),
+		   data->current_vector.id.c_str ());
+  else if (!data->current_vector.count.has_value ()
+	   && data->current_vector.locexpr.empty ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\" has neither a count attribute nor a count expression"),
+		   data->current_vector.id.c_str ());
+
+  if (data->current_vector.count.has_value ())
+    tdesc_create_vector (data->current_feature,
+			 data->current_vector.id.c_str (),
+			 data->current_vector.field_type,
+			 *data->current_vector.count);
+  else
+    {
+      tdesc_type *type = tdesc_create_vector (data->current_feature,
+					      data->current_vector.id.c_str (),
+					      data->current_vector.field_type,
+					      data->current_vector.locexpr,
+					      data->current_vector.locexpr_str);
+      tdesc_type_vector *type_vector = static_cast<tdesc_type_vector *> (type);
+
+      if (!data->current_vector.count_id.empty ())
+	type_vector->locexpr_id = data->current_vector.count_id;
+      else if (!data->current_vector.count_idref.empty ())
+	type_vector->locexpr_idref = data->current_vector.count_idref;
+
+      set_tdesc_include_dwarf_header (data->tdesc, true);
+    }
+
+  data->current_vector = {};
+}
+
+/* Handle the start of a <count> element.  Store any given count ID and process
+   IDREFs.  */
+
+static void
+tdesc_start_count (struct gdb_xml_parser *parser,
+		   const struct gdb_xml_element *element,
+		   void *user_data, std::vector<gdb_xml_value> &attributes)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+  const char *id = nullptr, *idref = nullptr;
+
+  for (gdb_xml_value &attr : attributes)
+    {
+      if (!strcmp (attr.name, "id"))
+	id = (const char *) attr.value.get ();
+      else if (!strcmp (attr.name, "idref"))
+	idref = (const char *) attr.value.get ();
+    }
+
+  if (id != nullptr && idref != nullptr)
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": count expression has both an id and an idref"),
+		   data->current_vector.id.c_str ());
+
+  if (id != nullptr)
+    {
+      std::string current_count_id = id;
+
+      if (data->vector_count_locexprs.find (current_count_id)
+	  != data->vector_count_locexprs.end ())
+	gdb_xml_error (parser,
+		       _ ("Vector \"%s\": count expression has duplicate id \"%s\""),
+		       data->current_vector.id.c_str (), id);
+
+      data->current_vector.count_id = std::move (current_count_id);
+    }
+  else if (idref != nullptr)
+    {
+      std::string current_count_idref = idref;
+
+      if (data->vector_count_locexprs.find (current_count_idref)
+	  == data->vector_count_locexprs.end ())
+	gdb_xml_error (parser,
+		       _ ("Vector \"%s\": count expression references unknown id \"%s\""),
+		       data->current_vector.id.c_str (), idref);
 
-  tdesc_create_vector (data->current_feature, id, field_type, count);
+      data->current_vector.count_idref = idref;
+    }
+}
+
+/* Handle the end of a <count> element.  If the element has an id attribute, store the
+   location expression in the map.  If it has an idref attribute, fetch the location
+   expression from the map.  */
+
+static void
+tdesc_end_count (struct gdb_xml_parser *parser,
+		 const struct gdb_xml_element *element, void *user_data,
+		 const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (!data->current_vector.count_id.empty ())
+    {
+      bool inserted
+	  = data->vector_count_locexprs
+		.insert ({ data->current_vector.count_id,
+			   data->current_vector.locexpr })
+		.second;
+
+      /* Insertion isn't supposed to fail: we already checked in the start
+	 handler that this id isn't present in the map.  */
+      gdb_assert (inserted);
+    }
+  else if (!data->current_vector.count_idref.empty ())
+    data->current_vector.locexpr
+	= data->vector_count_locexprs.at (data->current_vector.count_idref);
+}
+
+/* Handle the start of a <math> element.  It just has to check whether it's inside
+   a <count> element with an idref and initialize data->current_vector.locexpr_str.  */
+
+static void
+tdesc_start_math (struct gdb_xml_parser *parser,
+		  const struct gdb_xml_element *element, void *user_data,
+		  std::vector<gdb_xml_value> &attributes)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *)user_data;
+
+  if (!data->current_vector.count_idref.empty ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": count element with an idref isn't empty."),
+		   data->current_vector.id.c_str ());
+
+  data->current_vector.locexpr_str.emplace ();
+}
+
+/* Handle the end of an <apply> element.  We just need to store the expression
+   operator at the end of the location expression.  */
+
+static void
+tdesc_end_apply (struct gdb_xml_parser *parser,
+		 const struct gdb_xml_element *element, void *user_data,
+		 const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (!data->current_vector.apply_operator.has_value ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": apply element doesn't have an operator."),
+		   data->current_vector.id.c_str ());
+
+  data->current_vector.locexpr.push_back (*data->current_vector.apply_operator);
+  data->current_vector.locexpr_str->push_back (*data->current_vector.apply_operator_str);
+}
+
+/* Handle the end of a <ci> element and its value.  */
+
+static void
+tdesc_end_ci (struct gdb_xml_parser *parser,
+	      const struct gdb_xml_element *element, void *user_data,
+	      const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (data->preliminary_gdbarch == nullptr) {
+    gdbarch_info arch_info;
+    arch_info.bfd_arch_info = tdesc_architecture (data->tdesc);
+    arch_info.osabi = tdesc_osabi (data->tdesc);
+    data->preliminary_gdbarch = gdbarch_find_by_info (arch_info);
+
+    if (data->preliminary_gdbarch == nullptr)
+      gdb_xml_error (parser,
+		     _("Vector \"%s\": couldn't find basic architecture information."),
+		     data->current_vector.id.c_str ());
+
+    if (!gdbarch_regnum_to_dwarf2_reg_p (data->preliminary_gdbarch))
+      gdb_xml_error (parser,
+		     _("Vector \"%s\": architecture %s doesn't support vector count based on another register."),
+		     data->current_vector.id.c_str (),
+		     gdbarch_bfd_arch_info (data->preliminary_gdbarch)->printable_name);
+  }
+
+  ULONGEST regnum = gdb_xml_parse_ulongest (parser, body_text);
+  if (regnum > INT_MAX)
+    gdb_xml_error (parser,
+		   _("Vector \"%s\": count expression uses register number %s out of range."),
+		   data->current_vector.id.c_str (), pulongest (regnum));
+
+  int dwarf_reg = gdbarch_regnum_to_dwarf2_reg (data->preliminary_gdbarch,
+						regnum);
+  if (dwarf_reg < 0 || dwarf_reg > 255)
+    gdb_xml_error (parser,
+		   _("Vector \"%s\": count expression uses register number %s out of range."),
+		   data->current_vector.id.c_str (), pulongest (dwarf_reg));
+
+  data->current_vector.locexpr.push_back (DW_OP_bregx);
+  data->current_vector.locexpr.push_back ((gdb_byte) dwarf_reg);
+  data->current_vector.locexpr.push_back (0);
+
+  data->current_vector.locexpr_str->push_back ("DW_OP_bregx");
+  data->current_vector.locexpr_str->push_back (nullptr);
+  data->current_vector.locexpr_str->push_back (nullptr);
+
+  data->load_early_regnums.insert (regnum);
+}
+
+/* Handle the end of a <cn> element and its value.  */
+
+static void
+tdesc_end_cn (struct gdb_xml_parser *parser,
+	      const struct gdb_xml_element *element, void *user_data,
+	      const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+  ULONGEST number = gdb_xml_parse_ulongest (parser, body_text);
+
+  /* We only support DW_OP_lit0 to DW_OP_lit31.  */
+  if (number > 31)
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": count expression uses value %s larger than maximum of 31."),
+		   data->current_vector.id.c_str (), pulongest (number));
+
+  gdb_byte value = DW_OP_lit0 + number;
+  data->current_vector.locexpr.push_back (value);
+  data->current_vector.locexpr_str->push_back (get_DW_OP_name (value));
+}
+
+/* Handle the end of a <divide> element.  */
+
+static void
+tdesc_end_divide (struct gdb_xml_parser *parser,
+		  const struct gdb_xml_element *element, void *user_data,
+		  const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  data->current_vector.apply_operator = DW_OP_div;
+  data->current_vector.apply_operator_str = "DW_OP_div";
+}
+
+/* Handle the end of a <times> element.  */
+
+static void
+tdesc_end_times (struct gdb_xml_parser *parser,
+		 const struct gdb_xml_element *element, void *user_data,
+		 const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  data->current_vector.apply_operator = DW_OP_mul;
+  data->current_vector.apply_operator_str = "DW_OP_mul";
 }
 
 /* The elements and attributes of an XML target description.  */
@@ -525,6 +856,43 @@  static const struct gdb_xml_element enum_children[] = {
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
+/* MathML allows recursing <apply> elements, but we don't have a need for it
+   yet, so for simplicity we don't.  */
+static const struct gdb_xml_element apply_children[] = {
+  { "ci", nullptr, nullptr, GDB_XML_EF_NONE, nullptr, tdesc_end_ci },
+  { "cn", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_cn },
+  { "divide", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr,
+    tdesc_end_divide },
+  { "times", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_times },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element math_children[] = {
+  { "apply", nullptr, apply_children, GDB_XML_EF_OPTIONAL, nullptr,
+    tdesc_end_apply },
+  { "ci", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_ci },
+  { "cn", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_cn },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element count_children[] = {
+  { "math", nullptr, math_children, GDB_XML_EF_OPTIONAL, tdesc_start_math,
+    nullptr },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_attribute count_attributes[] = {
+  { "id", GDB_XML_AF_OPTIONAL, nullptr, nullptr },
+  { "idref", GDB_XML_AF_OPTIONAL, nullptr, nullptr },
+  { nullptr, GDB_XML_AF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element vector_children[] = {
+  { "count", count_attributes, count_children, GDB_XML_EF_OPTIONAL,
+    tdesc_start_count, tdesc_end_count },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
 static const struct gdb_xml_attribute reg_attributes[] = {
   { "name", GDB_XML_AF_NONE, NULL, NULL },
   { "bitsize", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
@@ -557,7 +925,7 @@  static const struct gdb_xml_attribute enum_attributes[] = {
 static const struct gdb_xml_attribute vector_attributes[] = {
   { "id", GDB_XML_AF_NONE, NULL, NULL },
   { "type", GDB_XML_AF_NONE, NULL, NULL },
-  { "count", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "count", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
@@ -582,9 +950,10 @@  static const struct gdb_xml_element feature_children[] = {
   { "enum", enum_attributes, enum_children,
     GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
     tdesc_start_enum, NULL },
-  { "vector", vector_attributes, NULL,
-    GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
-    tdesc_start_vector, NULL },
+  { "vector", vector_attributes, vector_children,
+    GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, tdesc_start_vector,
+    tdesc_end_vector },
+  { "architecture", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL, tdesc_end_arch },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -602,7 +971,7 @@  static const struct gdb_xml_element target_children[] = {
     NULL, tdesc_end_compatible },
   { "feature", feature_attributes, feature_children,
     GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
-    tdesc_start_feature, NULL },
+    tdesc_start_feature, tdesc_end_feature },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -636,7 +1005,6 @@  tdesc_parse_xml (const char *document, xml_fetch_another fetcher)
   if (it != xml_cache.end ())
     return it->second.get ();
 
-  memset (&data, 0, sizeof (struct tdesc_parsing_data));
   target_desc_up description = allocate_target_description ();
   data.tdesc = description.get ();
 
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 6148ccf9121b..3315a0c95f19 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -208,6 +208,7 @@  SFILES = \
 	$(srcdir)/linux-sparc-low.cc \
 	$(srcdir)/linux-x86-low.cc \
 	$(srcdir)/linux-xtensa-low.cc \
+	$(srcdir)/locexpr.cc \
 	$(srcdir)/mem-break.cc \
 	$(srcdir)/netbsd-aarch64-low.cc \
 	$(srcdir)/netbsd-amd64-low.cc \
@@ -262,6 +263,7 @@  OBS = \
 	dll.o \
 	hostio.o \
 	inferiors.o \
+	locexpr.o \
 	mem-break.o \
 	notif.o \
 	regcache.o \
diff --git a/gdbserver/config.in b/gdbserver/config.in
index 65f9ff6e6470..25aefbecae4f 100644
--- a/gdbserver/config.in
+++ b/gdbserver/config.in
@@ -104,6 +104,9 @@ 
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
+/* Define if the target provides DWARF register to regnum mapping. */
+#undef HAVE_DWARF_REG_TO_REGNUM
+
 /* Define to 1 if the system has the type `Elf32_auxv_t'. */
 #undef HAVE_ELF32_AUXV_T
 
diff --git a/gdbserver/configure b/gdbserver/configure
index 09cb3c5bf434..c310d522ef10 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -14743,6 +14743,12 @@  if $want_ipa ; then
    fi
 fi
 
+if test "${srv_dwarf_reg_to_regnum}" = "yes"; then
+
+$as_echo "#define HAVE_DWARF_REG_TO_REGNUM 1" >>confdefs.h
+
+fi
+
 
 
 
diff --git a/gdbserver/configure.ac b/gdbserver/configure.ac
index ee0de9decbde..35ec08da06e4 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -441,6 +441,11 @@  if $want_ipa ; then
    fi
 fi
 
+if test "${srv_dwarf_reg_to_regnum}" = "yes"; then
+  AC_DEFINE(HAVE_DWARF_REG_TO_REGNUM, 1,
+	    [Define if the target provides DWARF register to regnum mapping.])
+fi
+
 AC_SUBST(GDBSERVER_DEPFILES)
 AC_SUBST(GDBSERVER_LIBS)
 AC_SUBST(srv_xmlbuiltin)
diff --git a/gdbserver/locexpr.cc b/gdbserver/locexpr.cc
new file mode 100644
index 000000000000..25d614c3a2bb
--- /dev/null
+++ b/gdbserver/locexpr.cc
@@ -0,0 +1,107 @@ 
+/* Simplified DWARF location expression evaluator for gdbserver.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "server.h"
+#include "leb128.h"
+#include "locexpr.h"
+#include "regcache.h"
+#include "arch/aarch64.h"
+
+#define DW_OP_lit0 0x30
+#define DW_OP_lit2 0x32
+#define DW_OP_lit4 0x34
+#define DW_OP_lit8 0x38
+#define DW_OP_bregx 0x92
+#define DW_OP_div 0x1b
+#define DW_OP_mul 0x1e
+
+/* See gdbserver/locexpr.h.  */
+
+long
+evaluate_locexpr (const gdb::array_view<const gdb_byte> locexpr,
+		  const struct regcache *regcache)
+{
+  std::vector<long> stack;
+
+  for (auto it = locexpr.begin (); it != locexpr.end ();)
+    {
+      gdb_byte op = *it;
+      long result;
+
+      it++;
+      switch (op)
+	{
+	case DW_OP_div:
+	  {
+	    long divisor = stack.back ();
+	    stack.pop_back ();
+	    long dividend = stack.back ();
+	    stack.pop_back ();
+	    result = dividend / divisor;
+	    break;
+	  }
+	case DW_OP_mul:
+	  {
+	    long multiplicand = stack.back ();
+	    stack.pop_back ();
+	    long multiplier = stack.back ();
+	    stack.pop_back ();
+	    result = multiplier * multiplicand;
+	    break;
+	  }
+	case DW_OP_lit0:
+	case DW_OP_lit2:
+	case DW_OP_lit4:
+	case DW_OP_lit8:
+	  result = op - DW_OP_lit0;
+	  break;
+	case DW_OP_bregx:
+	  {
+	    uint64_t reg;
+	    int read = read_uleb128_to_uint64 (it, locexpr.end (), &reg);
+	    gdb_assert (read != 0);
+
+	    it += read;
+
+	    int64_t offset;
+	    read = read_sleb128_to_int64 (it, locexpr.end (), &offset);
+	    gdb_assert (read != 0);
+
+	    it += read;
+
+	    const char *arch = tdesc_architecture_name (regcache->tdesc);
+	    reg = tdesc_dwarf_reg_to_regnum (arch, reg);
+	    gdb_assert (reg != -1);
+
+	    register_status reg_status = regcache->get_register_status (reg);
+	    gdb_assert (reg_status == REG_VALID);
+	    regcache->raw_collect (reg,
+				   gdb::make_array_view ((gdb_byte *) &result,
+							 sizeof (result)));
+	    result += offset;
+	    break;
+	  }
+	default:
+	  gdb_assert_not_reached ("Unsupported locexpr operation: %c\n", op);
+	}
+
+      stack.push_back (result);
+    }
+
+  return stack.back ();
+}
diff --git a/gdbserver/locexpr.h b/gdbserver/locexpr.h
new file mode 100644
index 000000000000..7b768058697d
--- /dev/null
+++ b/gdbserver/locexpr.h
@@ -0,0 +1,31 @@ 
+/* Simplified DWARF location expression evaluator for gdbserver.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDBSERVER_LOCEXPR_H
+#define GDBSERVER_LOCEXPR_H
+
+#include "gdbsupport/array-view.h"
+#include "gdbsupport/common-types.h"
+#include "regcache.h"
+
+/* Evaluate the given DWARF LOCEXPR, using REGCACHE to get register values.  */
+
+long evaluate_locexpr (const gdb::array_view<const gdb_byte> locexpr,
+		       const struct regcache *regcache);
+
+#endif /* GDBSERVER_LOCEXPR_H */
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index 6f7ebb7c5c76..fbaf756f5050 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -129,6 +129,9 @@  copy_target_description (struct target_desc *dest,
   dest->expedite_regs = src->expedite_regs;
   dest->registers_size = src->registers_size;
   dest->xmltarget = src->xmltarget;
+
+  if (src->arch)
+    set_tdesc_architecture (dest, src->arch.get ());
 }
 
 const struct target_desc *
@@ -242,3 +245,18 @@  tdesc_contains_feature (const target_desc *tdesc, const std::string &feature)
 
   return false;
 }
+
+#ifndef HAVE_DWARF_REG_TO_REGNUM
+
+/* See gdbsupport/tdesc.h.
+
+   This is a dummy implementation.  It's not used if the target description
+   doesn't have variable-length registers.  */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+  return -1;
+}
+
+#endif /* HAVE_DWARF_REG_TO_REGNUM */
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index a99119274f44..05b348fd8077 100644
--- a/gdbsupport/tdesc.cc
+++ b/gdbsupport/tdesc.cc
@@ -18,6 +18,7 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "gdbsupport/tdesc.h"
+#include "dwarf2.h"
 
 tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_,
 		      int regnum, int save_restore_, const char *group_,
@@ -160,6 +161,21 @@  tdesc_create_vector (struct tdesc_feature *feature, const char *name,
 
 /* See gdbsupport/tdesc.h.  */
 
+struct tdesc_type *
+tdesc_create_vector (struct tdesc_feature *feature, const char *name,
+		     struct tdesc_type *field_type,
+		     const gdb::array_view<const gdb_byte> locexpr,
+		     std::optional<std::vector<const char *>> locexpr_str)
+{
+  tdesc_type_vector *type = new tdesc_type_vector (name, field_type, locexpr,
+						   locexpr_str);
+  feature->types.emplace_back (type);
+
+  return type;
+}
+
+/* See gdbsupport/tdesc.h.  */
+
 tdesc_type_with_fields *
 tdesc_create_struct (struct tdesc_feature *feature, const char *name)
 {
@@ -312,8 +328,113 @@  void print_xml_feature::visit (const tdesc_type_builtin *t)
 
 void print_xml_feature::visit (const tdesc_type_vector *t)
 {
-  add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
-	    t->name.c_str (), t->element_type->name.c_str (), t->count);
+  if (t->locexpr.empty ())
+    add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
+	      t->name.c_str (), t->element_type->name.c_str (), t->count);
+  else
+    {
+      add_line ("<vector id=\"%s\" type=\"%s\">",
+		t->name.c_str (), t->element_type->name.c_str ());
+
+      if (t->locexpr_idref.has_value ())
+	add_line ("  <count idref=\"%s\"/>", t->locexpr_idref->c_str ());
+      else
+	{
+	  if (t->locexpr_id.has_value ())
+	    add_line ("  <count id=\"%s\">", t->locexpr_id->c_str ());
+	  else
+	    add_line ("  <count>");
+
+	  add_line ("    <math>");
+
+	  std::vector<std::string> stack;
+
+	  /* Convert DWARF location expression to MathML.  */
+	  for (int i = 0; i < t->locexpr.size (); i++)
+	    {
+	      switch (t->locexpr[i])
+		{
+		case DW_OP_bregx:
+		  {
+		    gdb_byte arg1 = t->locexpr[i + 1];
+		    gdb_byte arg2 = t->locexpr[i + 2];
+		    gdb_byte dwarf_reg;
+
+		    if (arg1 == 0)
+		      dwarf_reg = arg2;
+		    else if (arg2 == 0)
+		      dwarf_reg = arg1;
+		    else
+		      error (_("In a vector count locexpr one of the "
+			       "operands of DW_OP_bregx must be 0."));
+
+		    if (m_arch == nullptr)
+		      error (_("Feature references a register in locexpr but "
+			       "the architecture is unknown."));
+
+		    int regnum = tdesc_dwarf_reg_to_regnum (m_arch, dwarf_reg);
+		    if (regnum == -1)
+		      error (_("Unknown DWARF register %d."), dwarf_reg);
+
+		    stack.push_back (string_printf ("<ci>%d</ci>", regnum));
+
+		    i += 2;
+		  }
+		  break;
+		case DW_OP_div:
+		  {
+		    std::string divisor = stack.back ();
+		    stack.pop_back ();
+		    std::string dividend = stack.back ();
+		    stack.pop_back ();
+
+		    stack.push_back ("</apply>");
+		    stack.push_back ("  " + divisor);
+		    stack.push_back ("  " + dividend);
+		    stack.push_back ("  <divide/>");
+		    stack.push_back ("<apply>");
+		  }
+		  break;
+		case DW_OP_mul:
+		  {
+		    std::string multiplicand = stack.back ();
+		    stack.pop_back ();
+		    std::string multiplier = stack.back ();
+		    stack.pop_back ();
+
+		    stack.push_back ("</apply>");
+		    stack.push_back ("  " + multiplicand);
+		    stack.push_back ("  " + multiplier);
+		    stack.push_back ("  <times/>");
+		    stack.push_back ("<apply>");
+		  }
+		  break;
+		default:
+		  {
+		    gdb_byte val = t->locexpr[i];
+
+		    if (val > DW_OP_lit0 && val <= DW_OP_lit31)
+		      stack.push_back (string_printf ("<cn>%d</cn>",
+						      val - DW_OP_lit0));
+		    else
+		      error (_ ("Unsupported DWARF operator %d."), val);
+		  }
+		  break;
+		}
+	    }
+
+	  while (!stack.empty ())
+	    {
+	      add_line ("      " + stack.back ());
+	      stack.pop_back ();
+	    }
+
+	  add_line ("    </math>");
+	  add_line ("  </count>");
+	}
+
+      add_line ("</vector>");
+    }
 }
 
 void print_xml_feature::visit (const tdesc_type_with_fields *t)
@@ -405,9 +526,12 @@  void print_xml_feature::visit_pre (const target_desc *e)
   add_line ("<!DOCTYPE target SYSTEM \"gdb-target.dtd\">");
   add_line ("<target>");
   indent (1);
-  if (tdesc_architecture_name (e))
-    add_line ("<architecture>%s</architecture>",
-	      tdesc_architecture_name (e));
+  const char *arch = tdesc_architecture_name (e);
+  if (arch != nullptr)
+    {
+      add_line ("<architecture>%s</architecture>", arch);
+      m_arch = arch;
+    }
 
   const char *osabi = tdesc_osabi_name (e);
   if (osabi != nullptr)
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index 7e483486139b..5f20e6ab8627 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -64,6 +64,11 @@  class tdesc_element
   virtual void accept (tdesc_element_visitor &v) const = 0;
 };
 
+/* Used in vector type element count or register bitsize to indicate that
+   the corresponding value is given by a DWARF expression.  */
+
+#define TDESC_REG_VARIABLE_SIZE -1
+
 /* An individual register from a target description.  */
 
 struct tdesc_reg : tdesc_element
@@ -242,13 +247,40 @@  struct tdesc_type_vector : tdesc_type
     element_type (element_type_), count (count_)
   {}
 
+  tdesc_type_vector (const std::string &name, tdesc_type *element_type_,
+		     const gdb::array_view<const gdb_byte> locexpr_,
+		     std::optional<std::vector<const char *>> locexpr_str_)
+      : tdesc_type (name, TDESC_TYPE_VECTOR), element_type (element_type_),
+	count (TDESC_REG_VARIABLE_SIZE), locexpr (locexpr_.begin (),
+						  locexpr_.end()),
+	locexpr_str (locexpr_str_)
+  {
+    if (locexpr_str.has_value () && locexpr.size () != locexpr_str->size ())
+      error (_ ("Vector locexpr and its string representation must have the same size."));
+  }
+
   void accept (tdesc_element_visitor &v) const override
   {
     v.visit (this);
   }
 
   struct tdesc_type *element_type;
+
+  /* Ignored if LOCEXPR isn't empty. */
   int count;
+
+  /* DWARF location expression providing number of elements.  */
+  gdb::byte_vector locexpr;
+
+  /* Vector with strings to use instead of the corresponding raw bytecode
+     in C code that creates the vector type.  */
+  std::optional<std::vector<const char *>> locexpr_str;
+
+  /* XML id used to reference this location expression.  */
+  std::optional<std::string> locexpr_id;
+
+  /* XML id of location expression to be used for number of elements.  */
+  std::optional<std::string> locexpr_idref;
 };
 
 /* A named type from a target description.  */
@@ -368,6 +400,14 @@  struct tdesc_type *tdesc_create_vector (struct tdesc_feature *feature,
 					struct tdesc_type *field_type,
 					int count);
 
+/* Return the created vector tdesc_type named NAME in FEATURE,
+   with number of elements given by DWARF LOCEXPR.  */
+struct tdesc_type * tdesc_create_vector (struct tdesc_feature *feature,
+					 const char *name,
+					 struct tdesc_type *field_type,
+					 const gdb::array_view<const gdb_byte> locexpr,
+					 std::optional<std::vector<const char *>> locexpr_str = {});
+
 /* Return the created struct tdesc_type named NAME in FEATURE.  */
 tdesc_type_with_fields *tdesc_create_struct (struct tdesc_feature *feature,
 					     const char *name);
@@ -469,6 +509,13 @@  class print_xml_feature : public tdesc_element_visitor
 
   /* The current indentation depth.  */
   int m_depth;
+
+  /* The current feature's architecture, if any.  */
+  const char *m_arch = nullptr;
 };
 
+/* Convert ARCH's DWARF register number DWARF_REG to GDB's register number.
+   Returns -1 if ARCH or DWARF_REG are unknown.  */
+int tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg);
+
 #endif /* COMMON_TDESC_H */