Message ID | 20241102025635.586759-10-thiago.bauermann@linaro.org |
---|---|
State | New |
Headers | show |
Series | gdbserver improvements for AArch64 SVE support | expand |
On 11/2/24 02:56, Thiago Jung Bauermann wrote: > 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. If we consider gdbserver only, it might not be a big deal to require some DWARF expression interpretation. But if we consider other debugging stubs from emulators (QEMU, FVP), debugging probes or even kgdb, the added complexity might prove a bit too much? > > ¹ 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 --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> ®isters = 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 (), ®); > + 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 */
Luis Machado <luis.machado@arm.com> writes: > On 11/2/24 02:56, Thiago Jung Bauermann wrote: >> 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. > > If we consider gdbserver only, it might not be a big deal to require some DWARF > expression interpretation. But if we consider other debugging stubs from emulators > (QEMU, FVP), debugging probes or even kgdb, the added complexity might prove a bit > too much? In theory yes, but if we document what GDB expects the remote stub to support I think it can be practical. For instance, in this patch the evaluate_locexpr () function in gdbserver has 70 lines. -- Thiago
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> ®isters = 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 (), ®); + 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 */