Boston, MA 02110-1301, USA. */
#include "defs.h"
+#include "gdbtypes.h"
#include "target.h"
#include "target-descriptions.h"
#include "xml-support.h"
#include "xml-tdesc.h"
+#include "filenames.h"
+
#include "gdb_assert.h"
#if !defined(HAVE_LIBEXPAT)
an XML parser. */
static struct target_desc *
-tdesc_parse_xml (const char *document)
+tdesc_parse_xml (const char *document, xml_fetch_another fetcher,
+ void *fetcher_baton)
{
static int have_warned;
#else /* HAVE_LIBEXPAT */
+/* A record of every XML description we have parsed. We never discard
+ old descriptions, because we never discard gdbarches. As long as we
+ have a gdbarch referencing this description, we want to have a copy
+ of it here, so that if we parse the same XML document again we can
+ return the same "struct target_desc *"; if they are not singletons,
+ then we will create unnecessary duplicate gdbarches. See
+ gdbarch_list_lookup_by_info. */
+
+struct tdesc_xml_cache
+{
+ const char *xml_document;
+ struct target_desc *tdesc;
+};
+typedef struct tdesc_xml_cache tdesc_xml_cache_s;
+DEF_VEC_O(tdesc_xml_cache_s);
+
+static VEC(tdesc_xml_cache_s) *xml_cache;
+
/* Callback data for target description parsing. */
struct tdesc_parsing_data
{
/* The target description we are building. */
struct target_desc *tdesc;
+
+ /* The target feature we are currently parsing, or last parsed. */
+ struct tdesc_feature *current_feature;
+
+ /* 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;
+
+ /* The union we are currently parsing, or last parsed. */
+ struct type *current_union;
};
/* Handle the end of an <architecture> element and its value. */
set_tdesc_architecture (data->tdesc, arch);
}
+/* Handle the start of a <feature> element. */
+
+static void
+tdesc_start_feature (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct tdesc_parsing_data *data = user_data;
+ char *name = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+
+ data->current_feature = tdesc_create_feature (data->tdesc, name);
+}
+
+/* Handle the start of a <reg> element. Fill in the optional
+ attributes and attach it to the containing feature. */
+
+static void
+tdesc_start_reg (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct tdesc_parsing_data *data = user_data;
+ struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes);
+ int ix = 0, length;
+ char *name, *group, *type;
+ int bitsize, regnum, save_restore;
+
+ length = VEC_length (gdb_xml_value_s, attributes);
+
+ name = attrs[ix++].value;
+ bitsize = * (ULONGEST *) attrs[ix++].value;
+
+ if (ix < length && strcmp (attrs[ix].name, "regnum") == 0)
+ regnum = * (ULONGEST *) attrs[ix++].value;
+ else
+ regnum = data->next_regnum;
+
+ if (ix < length && strcmp (attrs[ix].name, "type") == 0)
+ type = attrs[ix++].value;
+ else
+ type = "int";
+
+ if (ix < length && strcmp (attrs[ix].name, "group") == 0)
+ group = attrs[ix++].value;
+ else
+ group = NULL;
+
+ if (ix < length && strcmp (attrs[ix].name, "save-restore") == 0)
+ save_restore = * (ULONGEST *) attrs[ix++].value;
+ else
+ save_restore = 1;
+
+ if (strcmp (type, "int") != 0
+ && strcmp (type, "float") != 0
+ && strcmp (type, "code_ptr") != 0
+ && strcmp (type, "data_ptr") != 0
+ && tdesc_named_type (data->current_feature, type) == NULL)
+ gdb_xml_error (parser, _("Register \"%s\" has unknown type \"%s\""),
+ name, type);
+
+ tdesc_create_reg (data->current_feature, name, regnum, save_restore, group,
+ bitsize, type);
+
+ data->next_regnum = regnum + 1;
+}
+
+/* Handle the start of a <union> element. Initialize the type and
+ record it with the current feature. */
+
+static void
+tdesc_start_union (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct tdesc_parsing_data *data = user_data;
+ char *id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ struct type *type;
+
+ type = init_composite_type (NULL, TYPE_CODE_UNION);
+ TYPE_NAME (type) = xstrdup (id);
+ tdesc_record_type (data->current_feature, type);
+ data->current_union = type;
+}
+
+/* Handle the end of a <union> element. */
+
+static void
+tdesc_end_union (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct tdesc_parsing_data *data = user_data;
+ int i;
+
+ /* If any of the children of this union are vectors, flag the union
+ as a vector also. This allows e.g. a union of two vector types
+ to show up automatically in "info vector". */
+ for (i = 0; i < TYPE_NFIELDS (data->current_union); i++)
+ if (TYPE_VECTOR (TYPE_FIELD_TYPE (data->current_union, i)))
+ {
+ TYPE_FLAGS (data->current_union) |= TYPE_FLAG_VECTOR;
+ break;
+ }
+}
+
+/* Handle the start of a <field> element. Attach the field to the
+ current union. */
+
+static void
+tdesc_start_field (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct tdesc_parsing_data *data = user_data;
+ struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes);
+ struct type *type, *field_type;
+ char *field_name, *field_type_id;
+
+ field_name = attrs[0].value;
+ field_type_id = attrs[1].value;
+
+ field_type = tdesc_named_type (data->current_feature, field_type_id);
+ if (field_type == NULL)
+ gdb_xml_error (parser, _("Union field \"%s\" references undefined "
+ "type \"%s\""),
+ field_name, field_type_id);
+
+ append_composite_type_field (data->current_union, xstrdup (field_name),
+ field_type);
+}
+
+/* Handle the start of a <vector> element. Initialize the type and
+ record it with the current feature. */
+
+static void
+tdesc_start_vector (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct tdesc_parsing_data *data = user_data;
+ struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes);
+ struct type *type, *field_type, *range_type;
+ char *id, *field_type_id;
+ int count;
+
+ id = attrs[0].value;
+ field_type_id = attrs[1].value;
+ count = * (ULONGEST *) attrs[2].value;
+
+ field_type = tdesc_named_type (data->current_feature, field_type_id);
+ if (field_type == NULL)
+ gdb_xml_error (parser, _("Vector \"%s\" references undefined type \"%s\""),
+ id, field_type_id);
+
+ /* A vector is just an array plus a special flag. */
+ range_type = create_range_type (NULL, builtin_type_int, 0, count - 1);
+ type = create_array_type (NULL, field_type, range_type);
+ TYPE_NAME (type) = xstrdup (id);
+
+ TYPE_FLAGS (type) |= TYPE_FLAG_VECTOR;
+
+ tdesc_record_type (data->current_feature, type);
+}
+
/* The elements and attributes of an XML target description. */
-const struct gdb_xml_element target_children[] = {
+static const struct gdb_xml_attribute field_attributes[] = {
+ { "name", GDB_XML_AF_NONE, NULL, NULL },
+ { "type", GDB_XML_AF_NONE, NULL, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element union_children[] = {
+ { "field", field_attributes, NULL, GDB_XML_EF_REPEATABLE,
+ tdesc_start_field, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+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 },
+ { "regnum", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "type", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "group", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "save-restore", GDB_XML_AF_OPTIONAL,
+ gdb_xml_parse_attr_enum, gdb_xml_enums_boolean },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute union_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+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 },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute feature_attributes[] = {
+ { "name", GDB_XML_AF_NONE, NULL, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element feature_children[] = {
+ { "reg", reg_attributes, NULL,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_reg, NULL },
+ { "union", union_attributes, union_children,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_union, tdesc_end_union },
+ { "vector", vector_attributes, NULL,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_vector, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element target_children[] = {
{ "architecture", NULL, NULL, GDB_XML_EF_OPTIONAL,
NULL, tdesc_end_arch },
+ { "feature", feature_attributes, feature_children,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_feature, NULL },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
-const struct gdb_xml_element tdesc_elements[] = {
+static const struct gdb_xml_element tdesc_elements[] = {
{ "target", NULL, target_children, GDB_XML_EF_NONE,
NULL, NULL },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
/* Parse DOCUMENT into a target description and return it. */
static struct target_desc *
-tdesc_parse_xml (const char *document)
+tdesc_parse_xml (const char *document, xml_fetch_another fetcher,
+ void *fetcher_baton)
{
struct cleanup *back_to, *result_cleanup;
struct gdb_xml_parser *parser;
struct tdesc_parsing_data data;
+ struct tdesc_xml_cache *cache;
+ char *expanded_text;
+ int ix;
+
+ /* Expand all XInclude directives. */
+ expanded_text = xml_process_xincludes (_("target description"),
+ document, fetcher, fetcher_baton, 0);
+ if (expanded_text == NULL)
+ {
+ warning (_("Could not load XML target description; ignoring"));
+ return NULL;
+ }
- memset (&data, 0, sizeof (struct tdesc_parsing_data));
+ /* Check for an exact match in the list of descriptions we have
+ previously parsed. strcmp is a slightly inefficient way to
+ do this; an SHA-1 checksum would work as well. */
+ for (ix = 0; VEC_iterate (tdesc_xml_cache_s, xml_cache, ix, cache); ix++)
+ if (strcmp (cache->xml_document, expanded_text) == 0)
+ {
+ xfree (expanded_text);
+ return cache->tdesc;
+ }
back_to = make_cleanup (null_cleanup, NULL);
parser = gdb_xml_create_parser_and_cleanup (_("target description"),
tdesc_elements, &data);
+ gdb_xml_use_dtd (parser, "gdb-target.dtd");
+ memset (&data, 0, sizeof (struct tdesc_parsing_data));
data.tdesc = allocate_target_description ();
result_cleanup = make_cleanup_free_target_description (data.tdesc);
+ make_cleanup (xfree, expanded_text);
- if (gdb_xml_parse (parser, document) == 0)
+ if (gdb_xml_parse (parser, expanded_text) == 0)
{
/* Parsed successfully. */
+ struct tdesc_xml_cache new_cache;
+
+ new_cache.xml_document = expanded_text;
+ new_cache.tdesc = data.tdesc;
+ VEC_safe_push (tdesc_xml_cache_s, xml_cache, &new_cache);
discard_cleanups (result_cleanup);
do_cleanups (back_to);
return data.tdesc;
return NULL;
}
}
-
#endif /* HAVE_LIBEXPAT */
\f
the text. If something goes wrong, return NULL and warn. */
static char *
-fetch_xml_from_file (const char *filename)
+fetch_xml_from_file (const char *filename, void *baton)
{
+ const char *dirname = baton;
FILE *file;
struct cleanup *back_to;
char *text;
size_t len, offset;
- file = fopen (filename, FOPEN_RT);
- if (file == NULL)
+ if (dirname && *dirname)
{
- warning (_("Could not open \"%s\""), filename);
- return NULL;
+ char *fullname = concat (dirname, "/", filename, NULL);
+ if (fullname == NULL)
+ nomem (0);
+ file = fopen (fullname, FOPEN_RT);
+ xfree (fullname);
}
+ else
+ file = fopen (filename, FOPEN_RT);
+
+ if (file == NULL)
+ return NULL;
+
back_to = make_cleanup (do_cleanup_fclose, file);
/* Read in the whole file, one chunk at a time. */
struct target_desc *tdesc;
char *tdesc_str;
struct cleanup *back_to;
+ const char *base;
+ char *dirname;
- tdesc_str = fetch_xml_from_file (filename);
+ tdesc_str = fetch_xml_from_file (filename, NULL);
if (tdesc_str == NULL)
- return NULL;
+ {
+ warning (_("Could not open \"%s\""), filename);
+ return NULL;
+ }
back_to = make_cleanup (xfree, tdesc_str);
- tdesc = tdesc_parse_xml (tdesc_str);
+
+ /* Simple, portable version of dirname that does not modify its
+ argument. */
+ base = lbasename (filename);
+ while (base > filename && IS_DIR_SEPARATOR (base[-1]))
+ --base;
+ if (base > filename)
+ {
+ dirname = xmalloc (base - filename + 2);
+ memcpy (dirname, filename, base - filename);
+
+ /* On DOS based file systems, convert "d:foo" to "d:.", so that
+ we create "d:./bar" later instead of the (different)
+ "d:/bar". */
+ if (base - filename == 2 && IS_ABSOLUTE_PATH (base)
+ && !IS_DIR_SEPARATOR (filename[0]))
+ dirname[base++ - filename] = '.';
+
+ dirname[base - filename] = '\0';
+ make_cleanup (xfree, dirname);
+ }
+ else
+ dirname = NULL;
+
+ tdesc = tdesc_parse_xml (tdesc_str, fetch_xml_from_file, dirname);
do_cleanups (back_to);
return tdesc;
}
+/* Read a string representation of available features from the target,
+ using TARGET_OBJECT_AVAILABLE_FEATURES. The returned string is
+ malloc allocated and NUL-terminated. NAME should be a non-NULL
+ string identifying the XML document we want; the top level document
+ is "target.xml". Other calls may be performed for the DTD or
+ for <xi:include>. */
+
+static char *
+fetch_available_features_from_target (const char *name, void *baton_)
+{
+ struct target_ops *ops = baton_;
+
+ /* Read this object as a string. This ensures that a NUL
+ terminator is added. */
+ return target_read_stralloc (ops,
+ TARGET_OBJECT_AVAILABLE_FEATURES,
+ name);
+}
+\f
+
/* Read an XML target description using OPS. Parse it, and return the
parsed description. */
char *tdesc_str;
struct cleanup *back_to;
- tdesc_str = target_read_stralloc (ops, TARGET_OBJECT_AVAILABLE_FEATURES,
- "target.xml");
+ tdesc_str = fetch_available_features_from_target ("target.xml", ops);
if (tdesc_str == NULL)
return NULL;
back_to = make_cleanup (xfree, tdesc_str);
- tdesc = tdesc_parse_xml (tdesc_str);
+ tdesc = tdesc_parse_xml (tdesc_str,
+ fetch_available_features_from_target,
+ ops);
do_cleanups (back_to);
return tdesc;