diff options
Diffstat (limited to 'gdb/target-descriptions.c')
-rw-r--r-- | gdb/target-descriptions.c | 641 |
1 files changed, 639 insertions, 2 deletions
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c index 1100f5e..1f8cf7e 100644 --- a/gdb/target-descriptions.c +++ b/gdb/target-descriptions.c @@ -24,12 +24,17 @@ #include "defs.h" #include "arch-utils.h" #include "gdbcmd.h" +#include "gdbtypes.h" +#include "reggroups.h" #include "target.h" #include "target-descriptions.h" #include "vec.h" +#include "xml-support.h" #include "xml-tdesc.h" #include "gdb_assert.h" +#include "gdb_obstack.h" +#include "hashtab.h" /* Types. */ @@ -40,6 +45,69 @@ typedef struct property } property_s; DEF_VEC_O(property_s); +/* An individual register from a target description. */ + +typedef struct tdesc_reg +{ + /* The name of this register. In standard features, it may be + recognized by the architecture support code, or it may be purely + for the user. */ + char *name; + + /* The register number used by this target to refer to this + register. This is used for remote p/P packets and to determine + the ordering of registers in the remote g/G packets. */ + long target_regnum; + + /* If this flag is set, GDB should save and restore this register + around calls to an inferior function. */ + int save_restore; + + /* The name of the register group containing this register, or NULL + if the group should be automatically determined from the + register's type. If this is "general", "float", or "vector", the + corresponding "info" command should display this register's + value. It can be an arbitrary string, but should be limited to + alphanumeric characters and internal hyphens. Currently other + strings are ignored (treated as NULL). */ + char *group; + + /* The size of the register, in bits. */ + int bitsize; + + /* The type of the register. This string corresponds to either + a named type from the target description or a predefined + type from GDB. */ + char *type; + + /* The target-described type corresponding to TYPE, if found. */ + struct type *gdb_type; +} *tdesc_reg_p; +DEF_VEC_P(tdesc_reg_p); + +/* A named type from a target description. */ +typedef struct type *type_p; +DEF_VEC_P(type_p); + +/* A feature from a target description. Each feature is a collection + of other elements, e.g. registers and types. */ + +typedef struct tdesc_feature +{ + /* The name of this feature. It may be recognized by the architecture + support code. */ + char *name; + + /* The registers associated with this feature. */ + VEC(tdesc_reg_p) *registers; + + /* The types associated with this feature. */ + VEC(type_p) *types; +} *tdesc_feature_p; +DEF_VEC_P(tdesc_feature_p); + +/* A target description. */ + struct target_desc { /* The architecture reported by the target, if any. */ @@ -47,6 +115,30 @@ struct target_desc /* Any architecture-specific properties specified by the target. */ VEC(property_s) *properties; + + /* The features associated with this target. */ + VEC(tdesc_feature_p) *features; +}; + +/* Per-architecture data associated with a target description. The + target description may be shared by multiple architectures, but + this data is private to one gdbarch. */ + +struct tdesc_arch_data +{ + /* A list of registers, indexed by GDB's internal register number. + During initialization of the gdbarch this list is used to store + registers which the architecture assigns a fixed register number. + Registers which are NULL in this array, or off the end, are + treated as zero-sized and nameless (i.e. placeholders in the + numbering). */ + VEC(tdesc_reg_p) *registers; + + /* Functions which report the register name, type, and reggroups for + pseudo-registers. */ + gdbarch_register_name_ftype *pseudo_register_name; + gdbarch_register_type_ftype *pseudo_register_type; + gdbarch_register_reggroup_p_ftype *pseudo_register_reggroup_p; }; /* Global state. These variables are associated with the current @@ -72,6 +164,11 @@ static const struct target_desc *current_target_desc; static char *target_description_filename; +/* A handle for architecture-specific data associated with the + target description (see struct tdesc_arch_data). */ + +static struct gdbarch_data *tdesc_data; + /* Fetch the current target's description, and switch the current architecture to one which incorporates that description. */ @@ -116,7 +213,17 @@ target_find_description (void) gdbarch_info_init (&info); info.target_desc = current_target_desc; if (!gdbarch_update_p (info)) - warning (_("Could not use target-supplied description")); + warning (_("Architecture rejected target-supplied description")); + else + { + struct tdesc_arch_data *data; + + data = gdbarch_data (current_gdbarch, tdesc_data); + if (tdesc_has_registers (current_target_desc) + && data->registers == NULL) + warning (_("Target-supplied registers are not supported " + "by the current architecture")); + } } /* Now that we know this description is usable, record that we @@ -158,7 +265,7 @@ target_current_description (void) } -/* Direct accessors for feature sets. */ +/* Direct accessors for target descriptions. */ /* Return the string value of a property named KEY, or NULL if the property was not specified. */ @@ -187,8 +294,529 @@ tdesc_architecture (const struct target_desc *target_desc) } +/* Return 1 if this target description includes any registers. */ + +int +tdesc_has_registers (const struct target_desc *target_desc) +{ + int ix; + struct tdesc_feature *feature; + + if (target_desc == NULL) + return 0; + + for (ix = 0; + VEC_iterate (tdesc_feature_p, target_desc->features, ix, feature); + ix++) + if (! VEC_empty (tdesc_reg_p, feature->registers)) + return 1; + + return 0; +} + +/* Return the feature with the given name, if present, or NULL if + the named feature is not found. */ + +const struct tdesc_feature * +tdesc_find_feature (const struct target_desc *target_desc, + const char *name) +{ + int ix; + struct tdesc_feature *feature; + + for (ix = 0; + VEC_iterate (tdesc_feature_p, target_desc->features, ix, feature); + ix++) + if (strcmp (feature->name, name) == 0) + return feature; + + return NULL; +} + +/* Return the name of FEATURE. */ + +const char * +tdesc_feature_name (const struct tdesc_feature *feature) +{ + return feature->name; +} + +/* Return the type associated with ID in the context of FEATURE, or + NULL if none. */ + +struct type * +tdesc_named_type (const struct tdesc_feature *feature, const char *id) +{ + int ix; + struct type *gdb_type; + + /* First try target-defined types. */ + for (ix = 0; VEC_iterate (type_p, feature->types, ix, gdb_type); ix++) + if (strcmp (TYPE_NAME (gdb_type), id) == 0) + return gdb_type; + + /* Next try some predefined types. Note that none of these types + depend on the current architecture; some of the builtin_type_foo + variables are swapped based on the architecture. */ + if (strcmp (id, "int8") == 0) + return builtin_type_int8; + + if (strcmp (id, "int16") == 0) + return builtin_type_int16; + + if (strcmp (id, "int32") == 0) + return builtin_type_int32; + + if (strcmp (id, "int64") == 0) + return builtin_type_int64; + + if (strcmp (id, "uint8") == 0) + return builtin_type_uint8; + + if (strcmp (id, "uint16") == 0) + return builtin_type_uint16; + + if (strcmp (id, "uint32") == 0) + return builtin_type_uint32; + + if (strcmp (id, "uint64") == 0) + return builtin_type_uint64; + + if (strcmp (id, "code_ptr") == 0) + return builtin_type_void_func_ptr; + + if (strcmp (id, "data_ptr") == 0) + return builtin_type_void_data_ptr; + + if (strcmp (id, "arm_fpa_ext") == 0) + return builtin_type_arm_ext; + + return NULL; +} + + +/* Support for registers from target descriptions. */ + +/* Construct the per-gdbarch data. */ + +static void * +tdesc_data_init (struct obstack *obstack) +{ + struct tdesc_arch_data *data; + + data = OBSTACK_ZALLOC (obstack, struct tdesc_arch_data); + return data; +} + +/* Similar, but for the temporary copy used during architecture + initialization. */ + +struct tdesc_arch_data * +tdesc_data_alloc (void) +{ + return XZALLOC (struct tdesc_arch_data); +} + +/* Free something allocated by tdesc_data_alloc, if it is not going + to be used (for instance if it was unsuitable for the + architecture). */ + +void +tdesc_data_cleanup (void *data_untyped) +{ + struct tdesc_arch_data *data = data_untyped; + + VEC_free (tdesc_reg_p, data->registers); + xfree (data); +} + +/* Search FEATURE for a register named NAME. */ + +int +tdesc_numbered_register (const struct tdesc_feature *feature, + struct tdesc_arch_data *data, + int regno, const char *name) +{ + int ixr; + struct tdesc_reg *reg; + + for (ixr = 0; + VEC_iterate (tdesc_reg_p, feature->registers, ixr, reg); + ixr++) + if (strcasecmp (reg->name, name) == 0) + { + /* Make sure the vector includes a REGNO'th element. */ + while (regno >= VEC_length (tdesc_reg_p, data->registers)) + VEC_safe_push (tdesc_reg_p, data->registers, NULL); + VEC_replace (tdesc_reg_p, data->registers, regno, reg); + return 1; + } + + return 0; +} + +/* Search FEATURE for a register whose name is in NAMES. */ + +int +tdesc_numbered_register_choices (const struct tdesc_feature *feature, + struct tdesc_arch_data *data, + int regno, const char *const names[]) +{ + int i; + + for (i = 0; names[i] != NULL; i++) + if (tdesc_numbered_register (feature, data, regno, names[i])) + return 1; + + return 0; +} + +/* Look up a register by its GDB internal register number. */ + +static struct tdesc_reg * +tdesc_find_register (struct gdbarch *gdbarch, int regno) +{ + struct tdesc_reg *reg; + struct tdesc_arch_data *data; + + data = gdbarch_data (gdbarch, tdesc_data); + if (regno < VEC_length (tdesc_reg_p, data->registers)) + return VEC_index (tdesc_reg_p, data->registers, regno); + else + return NULL; +} + +static const char * +tdesc_register_name (int regno) +{ + struct tdesc_reg *reg = tdesc_find_register (current_gdbarch, regno); + int num_regs = gdbarch_num_regs (current_gdbarch); + int num_pseudo_regs = gdbarch_num_pseudo_regs (current_gdbarch); + + if (reg != NULL) + return reg->name; + + if (regno >= num_regs && regno < num_regs + num_pseudo_regs) + { + struct tdesc_arch_data *data = gdbarch_data (current_gdbarch, + tdesc_data); + gdb_assert (data->pseudo_register_name != NULL); + return data->pseudo_register_name (regno); + } + + return ""; +} + +static struct type * +tdesc_register_type (struct gdbarch *gdbarch, int regno) +{ + struct tdesc_reg *reg = tdesc_find_register (gdbarch, regno); + int num_regs = gdbarch_num_regs (gdbarch); + int num_pseudo_regs = gdbarch_num_pseudo_regs (gdbarch); + + if (reg == NULL && regno >= num_regs && regno < num_regs + num_pseudo_regs) + { + struct tdesc_arch_data *data = gdbarch_data (gdbarch, tdesc_data); + gdb_assert (data->pseudo_register_type != NULL); + return data->pseudo_register_type (gdbarch, regno); + } + + if (reg == NULL) + /* Return "int0_t", since "void" has a misleading size of one. */ + return builtin_type_int0; + + /* First check for a predefined or target defined type. */ + if (reg->gdb_type) + return reg->gdb_type; + + /* Next try size-sensitive type shortcuts. */ + if (strcmp (reg->type, "float") == 0) + { + if (reg->bitsize == gdbarch_float_bit (gdbarch)) + return builtin_type_float; + else if (reg->bitsize == gdbarch_double_bit (gdbarch)) + return builtin_type_double; + else if (reg->bitsize == gdbarch_long_double_bit (gdbarch)) + return builtin_type_long_double; + } + else if (strcmp (reg->type, "int") == 0) + { + if (reg->bitsize == gdbarch_long_bit (gdbarch)) + return builtin_type_long; + else if (reg->bitsize == TARGET_CHAR_BIT) + return builtin_type_char; + else if (reg->bitsize == gdbarch_short_bit (gdbarch)) + return builtin_type_short; + else if (reg->bitsize == gdbarch_int_bit (gdbarch)) + return builtin_type_int; + else if (reg->bitsize == gdbarch_long_long_bit (gdbarch)) + return builtin_type_long_long; + else if (reg->bitsize == gdbarch_ptr_bit (gdbarch)) + /* A bit desperate by this point... */ + return builtin_type_void_data_ptr; + } + else + internal_error (__FILE__, __LINE__, + "Register \"%s\" has an unknown type \"%s\"", + reg->name, reg->type); + + warning (_("Register \"%s\" has an unsupported size (%d bits)"), + reg->name, reg->bitsize); + return builtin_type_long; +} + +static int +tdesc_remote_register_number (struct gdbarch *gdbarch, int regno) +{ + struct tdesc_reg *reg = tdesc_find_register (gdbarch, regno); + + if (reg != NULL) + return reg->target_regnum; + else + return -1; +} + +/* Check whether REGNUM is a member of REGGROUP. Registers from the + target description may be classified as general, float, or vector. + Registers with no group specified go to the default reggroup + function and are handled by type. + + Arbitrary strings (other than "general", "float", and "vector") + from the description are not used; they cause the register to be + displayed in "info all-registers" but excluded from "info + registers" et al. The names of containing features are also not + used. This might be extended to display registers in some more + useful groupings. + + The save-restore flag is also implemented here. */ + +static int +tdesc_register_reggroup_p (struct gdbarch *gdbarch, int regno, + struct reggroup *reggroup) +{ + int num_regs = gdbarch_num_regs (gdbarch); + int num_pseudo_regs = gdbarch_num_pseudo_regs (gdbarch); + struct tdesc_reg *reg = tdesc_find_register (gdbarch, regno); + + if (reg == NULL && regno >= num_regs && regno < num_regs + num_pseudo_regs) + { + struct tdesc_arch_data *data = gdbarch_data (gdbarch, tdesc_data); + gdb_assert (data->pseudo_register_reggroup_p != NULL); + return data->pseudo_register_reggroup_p (gdbarch, regno, reggroup); + } + + if (reg != NULL && reg->group != NULL) + { + int general_p = 0, float_p = 0, vector_p = 0; + + if (strcmp (reg->group, "general") == 0) + general_p = 1; + else if (strcmp (reg->group, "float") == 0) + float_p = 1; + else if (strcmp (reg->group, "vector") == 0) + vector_p = 1; + + if (reggroup == float_reggroup) + return float_p; + + if (reggroup == vector_reggroup) + return vector_p; + + if (reggroup == general_reggroup) + return general_p; + } + + if (reg != NULL + && (reggroup == save_reggroup || reggroup == restore_reggroup)) + return reg->save_restore; + + return default_register_reggroup_p (gdbarch, regno, reggroup); +} + +/* Record architecture-specific functions to call for pseudo-register + support. */ + +void +set_tdesc_pseudo_register_name (struct gdbarch *gdbarch, + gdbarch_register_name_ftype *pseudo_name) +{ + struct tdesc_arch_data *data = gdbarch_data (gdbarch, tdesc_data); + + data->pseudo_register_name = pseudo_name; +} + +void +set_tdesc_pseudo_register_type (struct gdbarch *gdbarch, + gdbarch_register_type_ftype *pseudo_type) +{ + struct tdesc_arch_data *data = gdbarch_data (gdbarch, tdesc_data); + + data->pseudo_register_type = pseudo_type; +} + +void +set_tdesc_pseudo_register_reggroup_p + (struct gdbarch *gdbarch, + gdbarch_register_reggroup_p_ftype *pseudo_reggroup_p) +{ + struct tdesc_arch_data *data = gdbarch_data (gdbarch, tdesc_data); + + data->pseudo_register_reggroup_p = pseudo_reggroup_p; +} + +/* Update GDBARCH to use the target description for registers. */ + +void +tdesc_use_registers (struct gdbarch *gdbarch, + struct tdesc_arch_data *early_data) +{ + int num_regs = gdbarch_num_regs (gdbarch); + int i, ixf, ixr; + const struct target_desc *target_desc; + struct tdesc_feature *feature; + struct tdesc_reg *reg; + struct tdesc_arch_data *data; + htab_t reg_hash; + + target_desc = gdbarch_target_desc (gdbarch); + + /* We can't use the description for registers if it doesn't describe + any. This function should only be called after validating + registers, so the caller should know that registers are + included. */ + gdb_assert (tdesc_has_registers (target_desc)); + + data = gdbarch_data (gdbarch, tdesc_data); + data->registers = early_data->registers; + xfree (early_data); + + /* Build up a set of all registers, so that we can assign register + numbers where needed. The hash table expands as necessary, so + the initial size is arbitrary. */ + reg_hash = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL); + for (ixf = 0; + VEC_iterate (tdesc_feature_p, target_desc->features, ixf, feature); + ixf++) + for (ixr = 0; + VEC_iterate (tdesc_reg_p, feature->registers, ixr, reg); + ixr++) + { + void **slot = htab_find_slot (reg_hash, reg, INSERT); + + *slot = reg; + } + + /* Remove any registers which were assigned numbers by the + architecture. */ + for (ixr = 0; VEC_iterate (tdesc_reg_p, data->registers, ixr, reg); ixr++) + if (reg) + htab_remove_elt (reg_hash, reg); + + /* Assign numbers to the remaining registers and add them to the + list of registers. The new numbers are always above NUM_REGS. + Iterate over the features, not the hash table, so that the order + matches that in the target description. */ + + gdb_assert (VEC_length (tdesc_reg_p, data->registers) <= num_regs); + while (VEC_length (tdesc_reg_p, data->registers) < num_regs) + VEC_safe_push (tdesc_reg_p, data->registers, NULL); + for (ixf = 0; + VEC_iterate (tdesc_feature_p, target_desc->features, ixf, feature); + ixf++) + for (ixr = 0; + VEC_iterate (tdesc_reg_p, feature->registers, ixr, reg); + ixr++) + if (htab_find (reg_hash, reg) != NULL) + { + VEC_safe_push (tdesc_reg_p, data->registers, reg); + num_regs++; + } + + htab_delete (reg_hash); + + /* Update the architecture. */ + set_gdbarch_num_regs (gdbarch, num_regs); + set_gdbarch_register_name (gdbarch, tdesc_register_name); + set_gdbarch_register_type (gdbarch, tdesc_register_type); + set_gdbarch_remote_register_number (gdbarch, + tdesc_remote_register_number); + set_gdbarch_register_reggroup_p (gdbarch, tdesc_register_reggroup_p); +} + + /* Methods for constructing a target description. */ +static void +tdesc_free_reg (struct tdesc_reg *reg) +{ + xfree (reg->name); + xfree (reg->type); + xfree (reg->group); + xfree (reg); +} + +void +tdesc_create_reg (struct tdesc_feature *feature, const char *name, + int regnum, int save_restore, const char *group, + int bitsize, const char *type) +{ + struct tdesc_reg *reg = XZALLOC (struct tdesc_reg); + + reg->name = xstrdup (name); + reg->target_regnum = regnum; + reg->save_restore = save_restore; + reg->group = group ? xstrdup (group) : NULL; + reg->bitsize = bitsize; + reg->type = type ? xstrdup (type) : NULL; + + /* If the register's type is target-defined, look it up now. We may not + have easy access to the containing feature when we want it later. */ + reg->gdb_type = tdesc_named_type (feature, reg->type); + + VEC_safe_push (tdesc_reg_p, feature->registers, reg); +} + +static void +tdesc_free_feature (struct tdesc_feature *feature) +{ + struct tdesc_reg *reg; + int ix; + + for (ix = 0; VEC_iterate (tdesc_reg_p, feature->registers, ix, reg); ix++) + tdesc_free_reg (reg); + VEC_free (tdesc_reg_p, feature->registers); + + /* There is no easy way to free xmalloc-allocated types, nor is + there a way to allocate types on an obstack not associated with + an objfile. Therefore we never free types. Since we only ever + parse an identical XML document once, this memory leak is mostly + contained. */ + VEC_free (type_p, feature->types); + + xfree (feature->name); + xfree (feature); +} + +struct tdesc_feature * +tdesc_create_feature (struct target_desc *tdesc, const char *name) +{ + struct tdesc_feature *new_feature = XZALLOC (struct tdesc_feature); + + new_feature->name = xstrdup (name); + + VEC_safe_push (tdesc_feature_p, tdesc->features, new_feature); + return new_feature; +} + +void +tdesc_record_type (struct tdesc_feature *feature, struct type *type) +{ + /* The type's ID should be used as its TYPE_NAME. */ + gdb_assert (TYPE_NAME (type) != NULL); + + VEC_safe_push (type_p, feature->types, type); +} + struct target_desc * allocate_target_description (void) { @@ -199,10 +827,17 @@ static void free_target_description (void *arg) { struct target_desc *target_desc = arg; + struct tdesc_feature *feature; struct property *prop; int ix; for (ix = 0; + VEC_iterate (tdesc_feature_p, target_desc->features, ix, feature); + ix++) + tdesc_free_feature (feature); + VEC_free (tdesc_feature_p, target_desc->features); + + for (ix = 0; VEC_iterate (property_s, target_desc->properties, ix, prop); ix++) { @@ -305,6 +940,8 @@ unset_tdesc_filename_cmd (char *args, int from_tty) void _initialize_target_descriptions (void) { + tdesc_data = gdbarch_data_register_pre_init (tdesc_data_init); + add_prefix_cmd ("tdesc", class_maintenance, set_tdesc_cmd, _("\ Set target description specific variables."), &tdesc_set_cmdlist, "set tdesc ", |