/* XML target description support for GDB.
- Copyright (C) 2006
- Free Software Foundation, Inc.
+ Copyright (C) 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
Contributed by CodeSourcery.
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 2 of the License, or
+ 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,
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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "target.h"
#include "target-descriptions.h"
#include "xml-support.h"
#include "xml-tdesc.h"
+#include "osabi.h"
#include "filenames.h"
{
/* 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 struct or union we are currently parsing, or last parsed. */
+ struct tdesc_type *current_type;
+
+ /* The byte size of the current struct type, if specified. Zero
+ if not specified. */
+ int current_type_size;
+
+ /* Whether the current type is a flags type. */
+ int current_type_is_flags;
};
/* Handle the end of an <architecture> element and its value. */
set_tdesc_architecture (data->tdesc, arch);
}
+/* Handle the end of an <osabi> element and its value. */
+
+static void
+tdesc_end_osabi (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;
+ enum gdb_osabi osabi;
+
+ osabi = osabi_from_tdesc_string (body_text);
+ if (osabi == GDB_OSABI_UNKNOWN)
+ warning (_("Target description specified unknown osabi \"%s\""),
+ body_text);
+ else
+ set_tdesc_osabi (data->tdesc, osabi);
+}
+
+/* Handle the end of a <compatible> element and its value. */
+
+static void
+tdesc_end_compatible (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;
+ const struct bfd_arch_info *arch;
+
+ arch = bfd_scan_arch (body_text);
+ tdesc_add_compatible (data->tdesc, arch);
+}
+
+/* Handle the start of a <target> element. */
+
+static void
+tdesc_start_target (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ char *version = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+
+ if (strcmp (version, "1.0") != 0)
+ gdb_xml_error (parser,
+ _("Target description has unsupported version \"%s\""),
+ version);
+}
+
+/* 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
+ && 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;
+
+ data->current_type = tdesc_create_union (data->current_feature, id);
+ data->current_type_size = 0;
+ data->current_type_is_flags = 0;
+}
+
+/* Handle the start of a <struct> element. Initialize the type and
+ record it with the current feature. */
+
+static void
+tdesc_start_struct (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 tdesc_type *type;
+
+ type = tdesc_create_struct (data->current_feature, id);
+ data->current_type = type;
+ data->current_type_size = 0;
+ data->current_type_is_flags = 0;
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ {
+ int size = (int) * (ULONGEST *)
+ VEC_index (gdb_xml_value_s, attributes, 1)->value;
+
+ tdesc_set_struct_size (type, size);
+ data->current_type_size = size;
+ }
+}
+
+static void
+tdesc_start_flags (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;
+ int length = (int) * (ULONGEST *)
+ VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ struct tdesc_type *type;
+
+ type = tdesc_create_flags (data->current_feature, id, length);
+
+ data->current_type = type;
+ data->current_type_size = 0;
+ data->current_type_is_flags = 1;
+}
+
+/* Handle the start of a <field> element. Attach the field to the
+ current struct or 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;
+ int ix = 0, length;
+ struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes);
+ struct tdesc_type *field_type;
+ char *field_name, *field_type_id;
+ int start, end;
+
+ length = VEC_length (gdb_xml_value_s, attributes);
+
+ field_name = attrs[ix++].value;
+
+ if (ix < length && strcmp (attrs[ix].name, "type") == 0)
+ field_type_id = attrs[ix++].value;
+ else
+ field_type_id = NULL;
+
+ if (ix < length && strcmp (attrs[ix].name, "start") == 0)
+ start = * (ULONGEST *) attrs[ix++].value;
+ else
+ start = -1;
+
+ if (ix < length && strcmp (attrs[ix].name, "end") == 0)
+ end = * (ULONGEST *) attrs[ix++].value;
+ else
+ end = -1;
+
+ if (field_type_id != NULL)
+ {
+ if (data->current_type_is_flags)
+ gdb_xml_error (parser, _("Cannot add typed field \"%s\" to flags"),
+ field_name);
+ if (data->current_type_size != 0)
+ gdb_xml_error (parser,
+ _("Explicitly sized type can not contain non-bitfield \"%s\""),
+ field_name);
+
+ field_type = tdesc_named_type (data->current_feature, field_type_id);
+ if (field_type == NULL)
+ gdb_xml_error (parser, _("Field \"%s\" references undefined "
+ "type \"%s\""),
+ field_name, field_type_id);
+
+ tdesc_add_field (data->current_type, field_name, field_type);
+ }
+ else if (start != -1 && end != -1)
+ {
+ struct tdesc_type *t = data->current_type;
+
+ if (data->current_type_is_flags)
+ tdesc_add_flag (t, start, field_name);
+ else
+ {
+ if (data->current_type_size == 0)
+ gdb_xml_error (parser,
+ _("Implicitly sized type can not contain bitfield \"%s\""),
+ field_name);
+
+ if (end >= 64)
+ gdb_xml_error (parser,
+ _("Bitfield \"%s\" goes past 64 bits (unsupported)"),
+ field_name);
+
+ /* Assume that the bit numbering in XML is "lsb-zero". Most
+ architectures other than PowerPC use this ordering. In
+ the future, we can add an XML tag to indicate "msb-zero"
+ numbering. */
+ if (start > end)
+ gdb_xml_error (parser, _("Bitfield \"%s\" has start after end"),
+ field_name);
+
+ if (end >= data->current_type_size * TARGET_CHAR_BIT)
+ gdb_xml_error (parser, _("Bitfield \"%s\" does not fit in struct"));
+
+ tdesc_add_bitfield (t, field_name, start, end);
+ }
+ }
+ else
+ gdb_xml_error (parser, _("Field \"%s\" has neither type nor bit position"),
+ field_name);
+}
+
+/* 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 tdesc_type *field_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);
+
+ tdesc_create_vector (data->current_feature, id, field_type, count);
+}
+
/* 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_OPTIONAL, NULL, NULL },
+ { "start", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "end", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element struct_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 struct_union_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL},
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute flags_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, 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 },
+ { "struct", struct_union_attributes, struct_union_children,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_struct, NULL },
+ { "union", struct_union_attributes, struct_union_children,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_union, NULL },
+ { "flags", flags_attributes, struct_union_children,
+ GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ tdesc_start_flags, NULL },
+ { "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_attribute target_attributes[] = {
+ { "version", GDB_XML_AF_NONE, NULL, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element target_children[] = {
{ "architecture", NULL, NULL, GDB_XML_EF_OPTIONAL,
NULL, tdesc_end_arch },
+ { "osabi", NULL, NULL, GDB_XML_EF_OPTIONAL,
+ NULL, tdesc_end_osabi },
+ { "compatible", NULL, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
+ NULL, tdesc_end_compatible },
+ { "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[] = {
- { "target", NULL, target_children, GDB_XML_EF_NONE,
- NULL, NULL },
+static const struct gdb_xml_element tdesc_elements[] = {
+ { "target", target_attributes, target_children, GDB_XML_EF_NONE,
+ tdesc_start_target, NULL },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
#endif /* HAVE_LIBEXPAT */
\f
-/* Close FILE. */
-
-static void
-do_cleanup_fclose (void *file)
-{
- fclose (file);
-}
-
-/* Open FILENAME, read all its text into memory, close it, and return
- the text. If something goes wrong, return NULL and warn. */
-
-static char *
-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;
-
- if (dirname && *dirname)
- {
- 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. */
- len = 4096;
- offset = 0;
- text = xmalloc (len);
- make_cleanup (free_current_contents, &text);
- while (1)
- {
- size_t bytes_read;
-
- /* Continue reading where the last read left off. Leave at least
- one byte so that we can NUL-terminate the result. */
- bytes_read = fread (text + offset, 1, len - offset - 1, file);
- if (ferror (file))
- {
- warning (_("Read error from \"%s\""), filename);
- do_cleanups (back_to);
- return NULL;
- }
-
- offset += bytes_read;
-
- if (feof (file))
- break;
-
- len = len * 2;
- text = xrealloc (text, len);
- }
-
- fclose (file);
- discard_cleanups (back_to);
-
- text[offset] = '\0';
- return text;
-}
-
/* Read an XML target description from FILENAME. Parse it, and return
the parsed description. */
struct target_desc *tdesc;
char *tdesc_str;
struct cleanup *back_to;
- const char *base;
char *dirname;
- tdesc_str = fetch_xml_from_file (filename, NULL);
+ tdesc_str = xml_fetch_content_from_file (filename, NULL);
if (tdesc_str == NULL)
{
warning (_("Could not open \"%s\""), filename);
back_to = make_cleanup (xfree, 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;
+ dirname = ldirname (filename);
+ if (dirname != NULL)
+ make_cleanup (xfree, dirname);
- tdesc = tdesc_parse_xml (tdesc_str, fetch_xml_from_file, dirname);
+ tdesc = tdesc_parse_xml (tdesc_str, xml_fetch_content_from_file, dirname);
do_cleanups (back_to);
return tdesc;