blob: d6ccbc18f4e5f02441357bcc44ab78b4d58cbe92 [file] [log] [blame]
/* Support for runtime-defined target features for GDB.
Copyright (C) 2006
Free Software Foundation, Inc.
Contributed by CodeSourcery.
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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
#include "defs.h"
#include "arch-utils.h"
#include "exceptions.h"
#include "gdbtypes.h"
#include "reggroups.h"
#include "symfile.h"
#include "target.h"
#include "sha1.h"
#include "available.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "gdb_obstack.h"
#include "gdb_stdint.h"
/* TODO: Remote target "guess" features from g packet size */
/* TODO: Clarify warning messages. Remember, they will appear to
the user with no context after a "target remote". The user
doesn't know how we got into this code. */
/* Architecture TODO to support this:
- Call available_register_type from _register_type method.
- Handle unexpected changes to _num_regs.
- Call record_available_features from _gdbarch_init.
- Do not override the default _register_byte
(WARNING: This list is out of date and should be redone before submission.
And moved into gdbint.texi.)
*/
/* Support for caching XML objects read from the target.
TODO ITEMS:
- Support caching to disk.
- Support compiled-in feature cache.
- Figure out memory management for feature contents strings.
*/
/* Saved information about cached XML objects. Each cache entry
corresponds to a file in the cache, or an object fetched from
the target with one particular annex. */
struct xml_cache_entry
{
const char *annex;
const char *contents;
/* This flag is cleared when we begin reading features, and set
for new features when they are read. It is used to prevent
reading the same file from the target twice (for multiple
xi:include's or DTD references). */
int recently_used;
union
{
/* We use a union to represent the checksum in order to guarantee
sufficient alignment. */
uint32_t words[5];
unsigned char bytes[20];
} sha1sum;
struct xml_cache_entry *next;
};
/* A list of all the cached objects. */
static struct xml_cache_entry *xml_global_cache;
/* Look for a feature in the cache with ANNEX and CHECKSUM. If
CHECKSUM is NULL, then look for a feature in the cache which has
already been used this session. If no entry is found, return
NULL. */
static const char *
find_xml_feature_in_cache (const char *annex, const unsigned char *checksum)
{
struct xml_cache_entry *ent;
for (ent = xml_global_cache; ent != NULL; ent = ent->next)
{
if (strcmp (ent->annex, annex) != 0)
continue;
if (checksum == NULL && !ent->recently_used)
continue;
if (checksum != NULL && memcmp (ent->sha1sum.bytes, checksum, 20) != 0)
continue;
ent->recently_used = 1;
return ent->contents;
}
return NULL;
}
/* Add CONTENTS, which represents the object named ANNEX, to the
cache if it is not already cached. */
static void
add_xml_feature_to_cache (const char *annex, const char *contents)
{
struct xml_cache_entry new_ent;
/* FIXME: Again, memory allocation? */
new_ent.annex = xstrdup (annex);
new_ent.contents = xstrdup (contents);
sha1_buffer (new_ent.contents, strlen (new_ent.contents),
new_ent.sha1sum.bytes);
/* If this entry is already in the cache, do not add it again. This
call also marks the cache entry as used. */
if (find_xml_feature_in_cache (annex, new_ent.sha1sum.bytes))
return;
new_ent.recently_used = 1;
new_ent.next = xml_global_cache;
xml_global_cache = xmalloc (sizeof (struct xml_cache_entry));
memcpy (xml_global_cache, &new_ent, sizeof (struct xml_cache_entry));
}
/* Convert an ASCII checksum string, CHECKSUM, to a binary blob,
BYTES. Returns 0 for success, or -1 if a bad character is
encountered. CHECKSUM does not need to be NUL terminated. */
static int
checksum_to_bytes (char *checksum, unsigned char *bytes)
{
int i;
for (i = 0; i < 20; i++)
{
int n;
char c1, c2;
c1 = checksum[2 * i];
if (c1 >= '0' && c1 <= '9')
n = c1 - '0';
else if (c1 >= 'a' && c1 <= 'f')
n = c1 - 'a' + 10;
else if (c1 >= 'A' && c1 <= 'F')
n = c1 - 'A' + 10;
else
return -1;
n *= 16;
c2 = checksum[2 * i + 1];
if (c2 >= '0' && c2 <= '9')
n += c2 - '0';
else if (c2 >= 'a' && c2 <= 'f')
n += c2 - 'a' + 10;
else if (c2 >= 'A' && c2 <= 'F')
n += c2 - 'A' + 10;
else
return -1;
bytes[i] = n;
}
return 0;
}
/* Baton passed to fetch_available_features_from_target. */
struct fetch_features_baton
{
struct target_ops *ops;
struct fetch_features_checksum
{
const char *annex;
unsigned char checksum[20];
} *checksums;
};
/* 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 fetch_features_baton *baton = baton_;
char *features_str;
gdb_byte *features_buf;
LONGEST len;
const unsigned char *checksum = NULL;
const char *cached_str;
if (baton->checksums)
{
struct fetch_features_checksum *checksum_ent;
for (checksum_ent = baton->checksums;
checksum_ent->annex != NULL;
checksum_ent++)
if (strcmp (checksum_ent->annex, name) == 0)
break;
if (checksum_ent->annex)
checksum = checksum_ent->checksum;
}
cached_str = find_xml_feature_in_cache (name, checksum);
/* This function always returns something which the caller is
responsible for freeing. So, if we got a match, return a
copy of it. */
if (cached_str)
return xstrdup (cached_str);
len = target_read_whole (baton->ops, TARGET_OBJECT_AVAILABLE_FEATURES,
name, &features_buf);
if (len <= 0)
return NULL;
/* Since we decode this object as a string, simplify processing by
making sure it is NUL terminated. */
features_str = (char *) features_buf;
if (features_str[len - 1] != '\0')
{
features_str = xrealloc (features_str, len + 1);
features_str[len] = '\0';
}
if (baton->checksums)
add_xml_feature_to_cache (name, features_str);
return features_str;
}
/* Standard method to convert a string representation of available features
to a binary representation. The string representation is fetched using
TARGET_OBJECT_AVAILABLE_FEATURES. */
/* TODO: Document \n conventions */
struct gdb_feature_set *
available_features_from_target_object (struct target_ops *ops,
struct obstack *obstack)
{
struct fetch_features_baton baton;
struct gdb_feature_set *features;
char *features_str, *checksums_str;
int ret;
struct xml_cache_entry *ent;
struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
/* Reset the recently used flag so that we read any objects
without checksums from the target. */
for (ent = xml_global_cache; ent != NULL; ent = ent->next)
ent->recently_used = 0;
/* Initialize the baton. */
baton.ops = ops;
baton.checksums = NULL;
/* Attempt to read checksums from the target. */
checksums_str = fetch_available_features_from_target ("CHECKSUMS", &baton);
if (checksums_str)
{
char *p;
int n_checksums;
make_cleanup (xfree, checksums_str);
/* Allow for one checksum in case there is no trailing newline,
and one to serve as a NULL terminator. */
n_checksums = 2;
/* Allocate one additional checksum per newline. */
for (p = checksums_str; *p; p++)
if (*p == '\n')
n_checksums++;
baton.checksums = xmalloc (n_checksums
* sizeof (struct fetch_features_checksum));
make_cleanup (xfree, baton.checksums);
n_checksums = 0;
p = checksums_str;
while (*p)
{
char *field_end;
/* Find the first space on the line, marking the end of the
checksum. */
field_end = p;
while (*field_end && *field_end != '\n'
&& *field_end != ' ')
field_end++;
/* Check for a malformed checksum. */
if (*field_end != ' '
|| field_end - p != 40
|| checksum_to_bytes (p, baton.checksums[n_checksums].checksum))
{
/* Skip this line. */
p = field_end;
while (*p && *p != '\n')
p++;
if (*p == '\n')
p++;
continue;
}
*field_end = '\0';
/* Skip whitespace after the checksum. */
p = field_end + 1;
while (*p == ' ')
p++;
field_end = p;
while (*field_end && *field_end != '\n')
field_end++;
if (field_end == p)
{
/* Malformed line; skip it. */
if (*p == '\n')
p++;
continue;
}
baton.checksums[n_checksums++].annex = p;
/* Advance to the next line, inserting a NUL for the end of
the annex name if necessary. */
if (*field_end)
{
*field_end = '\0';
p = field_end + 1;
}
else
break;
}
baton.checksums[n_checksums].annex = NULL;
}
/* FIXME: Memory management: what happens to features_str? */
features_str = fetch_available_features_from_target ("target.xml", &baton);
if (features_str == NULL)
return NULL;
features = OBSTACK_ZALLOC (obstack, struct gdb_feature_set);
features->obstack = obstack;
ret = available_features_from_xml_string (features,
features_str,
fetch_available_features_from_target,
&baton, 0);
do_cleanups (back_to);
if (ret < 0)
{
warning (_("Could not parse features XML from target"));
return NULL;
}
return features;
}
/* Return non-zero if LHS and RHS refer to compatible feature sets. */
int
features_same_p (const struct gdb_feature_set *lhs,
const struct gdb_feature_set *rhs)
{
/* Two feature sets are the same if and only if they are described
by the same XML. */
if (memcmp (lhs->checksum, rhs->checksum, 20) == 0)
return 1;
else
return 0;
}
/* Switch the architecture (gdbarch) to one which supports FEATURES. */
void
arch_set_available_features (struct gdb_feature_set *features)
{
volatile struct gdb_exception e;
struct gdbarch_info info;
TRY_CATCH (e, RETURN_MASK_ERROR)
{
gdbarch_info_init (&info);
info.feature_set = features;
if (!gdbarch_update_p (info))
internal_error (__FILE__, __LINE__, "could not update architecture");
}
if (e.reason == RETURN_ERROR)
exception_fprintf (gdb_stderr, e,
_("warning: could not use supplied target description: "));
}
static struct gdb_feature_set *
copy_features_to_obstack (struct obstack *obstack,
const struct gdb_feature_set *features)
{
struct gdb_feature_set *result;
struct gdb_available_feature **slot, *orig_feature;
result = obstack_alloc (obstack, sizeof (struct gdb_feature_set));
result->obstack = obstack;
slot = &result->features;
for (orig_feature = features->features;
orig_feature;
orig_feature = orig_feature->next)
{
struct gdb_available_feature *feature;
struct gdb_available_register **reg_slot, *orig_reg;
feature = OBSTACK_ZALLOC (obstack, struct gdb_available_feature);
*slot = feature;
slot = &feature->next;
memcpy (feature, orig_feature, sizeof (struct gdb_available_feature));
feature->name = obsavestring (feature->name, strlen (feature->name),
obstack);
if (feature->arch_data)
feature->arch_data = obsavestring (feature->arch_data,
strlen (feature->arch_data),
obstack);
reg_slot = &feature->registers;
for (orig_reg = orig_feature->registers;
orig_reg;
orig_reg = orig_reg->next)
{
struct gdb_available_register *reg;
reg = OBSTACK_ZALLOC (obstack, struct gdb_available_register);
*reg_slot = reg;
reg_slot = &reg->next;
memcpy (reg, orig_reg, sizeof (struct gdb_available_register));
reg->name = obsavestring (reg->name, strlen (reg->name), obstack);
if (reg->arch_data)
reg->arch_data = obsavestring (reg->arch_data,
strlen (reg->arch_data),
obstack);
if (reg->group)
reg->group = obsavestring (reg->group, strlen (reg->group),
obstack);
if (reg->type)
reg->type = obsavestring (reg->type, strlen (reg->type),
obstack);
}
}
return result;
}
/* Set an architecture's feature set. Store BASE_FEATURES in GDBARCH,
and on the correct obstack.
This function will update GDBARCH's num_regs. It is the
architecture's responsibility to handle this if it has pseudo
registers. Before calling this function, num_regs should be
the number of fixed registers handled by the target code; all
unassigned registers will be given numbers above that point. */
void
record_available_features (struct gdbarch *gdbarch,
struct gdb_feature_set *base_features)
{
struct gdb_available_feature *feature;
struct gdb_available_register *reg;
struct gdb_feature_set *features;
int gdb_regnum;
features = copy_features_to_obstack (gdbarch_obstack (gdbarch),
base_features);
set_gdbarch_feature_set (gdbarch, features);
gdb_regnum = gdbarch_num_regs (gdbarch);
for (feature = features->features; feature; feature = feature->next)
for (reg = feature->registers; reg; reg = reg->next)
if (reg->gdb_regnum == -1)
reg->gdb_regnum = gdb_regnum++;
set_gdbarch_num_regs (gdbarch, gdb_regnum);
}
/* Search FEATURES for a register with GDB register number REGNUM. */
static struct gdb_available_register *
find_register (const struct gdb_feature_set *features, int regnum)
{
struct gdb_available_feature *feature;
struct gdb_available_register *reg;
if (features == NULL)
return NULL;
for (feature = features->features; feature; feature = feature->next)
for (reg = feature->registers; reg; reg = reg->next)
if (reg->gdb_regnum == regnum)
return reg;
return NULL;
}
/* Search FEATURES for a register with target-specified name NAME, and
set its GDB register number to REGNUM. Pass REGNUM == -1 if you do
not need to fix a register number for this register. Return 1 if
the register was found, and 0 if it was not. This function should
only be used while initializing a gdbarch. */
int
available_find_named_register (struct gdb_feature_set *features,
const char *name, int regnum)
{
struct gdb_available_feature *feature;
struct gdb_available_register *reg;
if (features == NULL)
return 0;
for (feature = features->features; feature; feature = feature->next)
for (reg = feature->registers; reg; reg = reg->next)
if (strcmp (reg->name, name) == 0)
{
reg->gdb_regnum = regnum;
return 1;
}
/* FIXME: Should we sanity check the target-supplied data here for
duplicate register names? Right now GDB can't handle duplicated
register names at all, but in the future it may. */
return 0;
}
/* Search FEATURES for a feature with the well-known name NAME,
which GDB may have special support for. */
int
available_find_named_feature (struct gdb_feature_set *features,
const char *name)
{
struct gdb_available_feature *feature;
if (features == NULL)
return 0;
for (feature = features->features; feature; feature = feature->next)
if (strcmp (feature->name, name) == 0)
return 1;
return 0;
}
/* Return the type of target-described register REGNUM, if the feature set
for GDBARCH describes that register. Otherwise return NULL. */
struct type *
available_register_type (struct gdbarch *gdbarch, int regnum)
{
struct gdb_available_register *reg;
reg = find_register (gdbarch_feature_set (gdbarch), regnum);
if (reg == NULL)
return NULL;
if (reg->type && 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;
}
if (reg->type && strcmp (reg->type, "int") != 0)
{
/* FIXME: Warn the user about an unknown type + size? */
}
/* Use an integer type; default to "long". */
if (reg->bitsize == gdbarch_long_bit (gdbarch))
return builtin_type_long;
else if (reg->bitsize == TARGET_CHAR_BIT)
return builtin_type_signed_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
{
/* FIXME: Create a new integer type of the appropriate size? */
internal_error (__FILE__, __LINE__,
_("GDB does not support %ld-bit registers on this target"),
reg->bitsize);
}
}
/* Return the name of target-described register REGNUM, if the feature set
for GDBARCH describes that register. Otherwise return NULL. */
const char *
available_register_name (struct gdbarch *gdbarch, int regnum)
{
struct gdb_available_register *reg;
reg = find_register (gdbarch_feature_set (gdbarch), regnum);
if (reg == NULL)
return NULL;
return reg->name;
}
/* Return the target-supplied register of target-described register
REGNUM, or -1 if the register can not be accessed. */
int
available_register_target_regnum (struct gdbarch *gdbarch, int regnum)
{
struct gdb_available_register *reg;
/* If there is no feature set, use the legacy 1:1 mapping. */
if (gdbarch_feature_set (gdbarch) == NULL)
return regnum;
reg = find_register (gdbarch_feature_set (gdbarch), regnum);
if (reg == NULL)
return -1;
return reg->protocol_number;
}
/* Check whether REGNUM is a member of REGGROUP. */
/* TODO: This function only supports "info registers", "info float",
and "info vector". Registers with group="general" go in general;
group="float" and group="vector" are similar. Other specified
values of group go into all-registers only. Registers with no
group specified go to the default function and are handled by
type. When we have a hierarchy of features, it may make more
sense to use that to show registers. */
int
available_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
struct reggroup *reggroup)
{
struct gdb_available_register *reg;
reg = find_register (gdbarch_feature_set (gdbarch), regnum);
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, regnum, reggroup);
}