Add "make pdf" and "make install-pdf", from Brooks Moses
[deliverable/binutils-gdb.git] / gdb / xml-tdesc.c
index f47e3687b8194a9fd4aa08a371c0bf4e693a6502..a4e1fe9a72e3afe8c60d3f4fd2ed67fb64fdd914 100644 (file)
    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)
@@ -36,7 +39,8 @@
    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;
 
@@ -52,12 +56,40 @@ tdesc_parse_xml (const char *document)
 
 #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.  */
@@ -77,15 +109,235 @@ tdesc_end_arch (struct gdb_xml_parser *parser,
   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 }
@@ -94,24 +346,53 @@ const struct gdb_xml_element tdesc_elements[] = {
 /* 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;
@@ -123,7 +404,6 @@ tdesc_parse_xml (const char *document)
       return NULL;
     }
 }
-
 #endif /* HAVE_LIBEXPAT */
 \f
 
@@ -139,19 +419,28 @@ do_cleanup_fclose (void *file)
    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.  */
@@ -198,18 +487,67 @@ file_read_description_xml (const char *filename)
   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.  */
 
@@ -220,13 +558,14 @@ target_read_description_xml (struct target_ops *ops)
   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;
This page took 0.027704 seconds and 4 git commands to generate.