mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-06 12:09:26 +08:00
bd2b40ac12
This changes GDB to use frame_info_ptr instead of frame_info * The substitution was done with multiple sequential `sed` commands: sed 's/^struct frame_info;/class frame_info_ptr;/' sed 's/struct frame_info \*/frame_info_ptr /g' - which left some issues in a few files, that were manually fixed. sed 's/\<frame_info \*/frame_info_ptr /g' sed 's/frame_info_ptr $/frame_info_ptr/g' - used to remove whitespace problems. The changed files were then manually checked and some 'sed' changes undone, some constructors and some gets were added, according to what made sense, and what Tromey originally did Co-Authored-By: Bruno Larsen <blarsen@redhat.com> Approved-by: Tom Tomey <tom@tromey.com>
240 lines
6.6 KiB
C
240 lines
6.6 KiB
C
/* User visible, per-frame registers, for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 2002-2022 Free Software Foundation, Inc.
|
|
|
|
Contributed by Red Hat.
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "user-regs.h"
|
|
#include "gdbtypes.h"
|
|
#include "frame.h"
|
|
#include "arch-utils.h"
|
|
#include "command.h"
|
|
#include "cli/cli-cmds.h"
|
|
|
|
/* A table of user registers.
|
|
|
|
User registers have regnum's that live above of the range [0
|
|
.. gdbarch_num_regs + gdbarch_num_pseudo_regs)
|
|
(which is controlled by the target).
|
|
The target should never see a user register's regnum value.
|
|
|
|
Always append, never delete. By doing this, the relative regnum
|
|
(offset from gdbarch_num_regs + gdbarch_num_pseudo_regs)
|
|
assigned to each user register never changes. */
|
|
|
|
struct user_reg
|
|
{
|
|
const char *name;
|
|
/* Avoid the "read" symbol name as it conflicts with a preprocessor symbol
|
|
in the NetBSD header for Stack Smashing Protection, that wraps the read(2)
|
|
syscall. */
|
|
struct value *(*xread) (frame_info_ptr frame, const void *baton);
|
|
const void *baton;
|
|
struct user_reg *next;
|
|
};
|
|
|
|
/* This structure is named gdb_user_regs instead of user_regs to avoid
|
|
conflicts with any "struct user_regs" in system headers. For instance,
|
|
on ARM GNU/Linux native builds, nm-linux.h includes <signal.h> includes
|
|
<sys/ucontext.h> includes <sys/procfs.h> includes <sys/user.h>, which
|
|
declares "struct user_regs". */
|
|
|
|
struct gdb_user_regs
|
|
{
|
|
struct user_reg *first = nullptr;
|
|
struct user_reg **last = nullptr;
|
|
};
|
|
|
|
static void
|
|
append_user_reg (struct gdb_user_regs *regs, const char *name,
|
|
user_reg_read_ftype *xread, const void *baton,
|
|
struct user_reg *reg)
|
|
{
|
|
/* The caller is responsible for allocating memory needed to store
|
|
the register. By doing this, the function can operate on a
|
|
register list stored in the common heap or a specific obstack. */
|
|
gdb_assert (reg != NULL);
|
|
reg->name = name;
|
|
reg->xread = xread;
|
|
reg->baton = baton;
|
|
reg->next = NULL;
|
|
if (regs->last == nullptr)
|
|
regs->last = ®s->first;
|
|
(*regs->last) = reg;
|
|
regs->last = &(*regs->last)->next;
|
|
}
|
|
|
|
/* An array of the builtin user registers. */
|
|
|
|
static struct gdb_user_regs builtin_user_regs;
|
|
|
|
void
|
|
user_reg_add_builtin (const char *name, user_reg_read_ftype *xread,
|
|
const void *baton)
|
|
{
|
|
append_user_reg (&builtin_user_regs, name, xread, baton,
|
|
XNEW (struct user_reg));
|
|
}
|
|
|
|
/* Per-architecture user registers. Start with the builtin user
|
|
registers and then, again, append. */
|
|
|
|
static const registry<gdbarch>::key<gdb_user_regs> user_regs_data;
|
|
|
|
static gdb_user_regs *
|
|
get_user_regs (struct gdbarch *gdbarch)
|
|
{
|
|
struct gdb_user_regs *regs = user_regs_data.get (gdbarch);
|
|
if (regs == nullptr)
|
|
{
|
|
regs = new struct gdb_user_regs;
|
|
|
|
struct obstack *obstack = gdbarch_obstack (gdbarch);
|
|
regs->last = ®s->first;
|
|
for (user_reg *reg = builtin_user_regs.first;
|
|
reg != NULL;
|
|
reg = reg->next)
|
|
append_user_reg (regs, reg->name, reg->xread, reg->baton,
|
|
OBSTACK_ZALLOC (obstack, struct user_reg));
|
|
user_regs_data.set (gdbarch, regs);
|
|
}
|
|
|
|
return regs;
|
|
}
|
|
|
|
void
|
|
user_reg_add (struct gdbarch *gdbarch, const char *name,
|
|
user_reg_read_ftype *xread, const void *baton)
|
|
{
|
|
struct gdb_user_regs *regs = get_user_regs (gdbarch);
|
|
gdb_assert (regs != NULL);
|
|
append_user_reg (regs, name, xread, baton,
|
|
GDBARCH_OBSTACK_ZALLOC (gdbarch, struct user_reg));
|
|
}
|
|
|
|
int
|
|
user_reg_map_name_to_regnum (struct gdbarch *gdbarch, const char *name,
|
|
int len)
|
|
{
|
|
/* Make life easy, set the len to something reasonable. */
|
|
if (len < 0)
|
|
len = strlen (name);
|
|
|
|
/* Search register name space first - always let an architecture
|
|
specific register override the user registers. */
|
|
{
|
|
int maxregs = gdbarch_num_cooked_regs (gdbarch);
|
|
|
|
for (int i = 0; i < maxregs; i++)
|
|
{
|
|
const char *regname = gdbarch_register_name (gdbarch, i);
|
|
|
|
if (len == strlen (regname) && strncmp (regname, name, len) == 0)
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/* Search the user name space. */
|
|
{
|
|
struct gdb_user_regs *regs = get_user_regs (gdbarch);
|
|
struct user_reg *reg;
|
|
int nr;
|
|
|
|
for (nr = 0, reg = regs->first; reg != NULL; reg = reg->next, nr++)
|
|
{
|
|
if ((len < 0 && strcmp (reg->name, name))
|
|
|| (len == strlen (reg->name)
|
|
&& strncmp (reg->name, name, len) == 0))
|
|
return gdbarch_num_cooked_regs (gdbarch) + nr;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static struct user_reg *
|
|
usernum_to_user_reg (struct gdbarch *gdbarch, int usernum)
|
|
{
|
|
struct gdb_user_regs *regs = get_user_regs (gdbarch);
|
|
struct user_reg *reg;
|
|
|
|
for (reg = regs->first; reg != NULL; reg = reg->next)
|
|
{
|
|
if (usernum == 0)
|
|
return reg;
|
|
usernum--;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
user_reg_map_regnum_to_name (struct gdbarch *gdbarch, int regnum)
|
|
{
|
|
int maxregs = gdbarch_num_cooked_regs (gdbarch);
|
|
|
|
if (regnum < 0)
|
|
return NULL;
|
|
else if (regnum < maxregs)
|
|
return gdbarch_register_name (gdbarch, regnum);
|
|
else
|
|
{
|
|
struct user_reg *reg = usernum_to_user_reg (gdbarch, regnum - maxregs);
|
|
if (reg == NULL)
|
|
return NULL;
|
|
else
|
|
return reg->name;
|
|
}
|
|
}
|
|
|
|
struct value *
|
|
value_of_user_reg (int regnum, frame_info_ptr frame)
|
|
{
|
|
struct gdbarch *gdbarch = get_frame_arch (frame);
|
|
int maxregs = gdbarch_num_cooked_regs (gdbarch);
|
|
struct user_reg *reg = usernum_to_user_reg (gdbarch, regnum - maxregs);
|
|
|
|
gdb_assert (reg != NULL);
|
|
return reg->xread (frame, reg->baton);
|
|
}
|
|
|
|
static void
|
|
maintenance_print_user_registers (const char *args, int from_tty)
|
|
{
|
|
struct gdbarch *gdbarch = get_current_arch ();
|
|
struct user_reg *reg;
|
|
int regnum;
|
|
|
|
struct gdb_user_regs *regs = get_user_regs (gdbarch);
|
|
regnum = gdbarch_num_cooked_regs (gdbarch);
|
|
|
|
gdb_printf (" %-11s %3s\n", "Name", "Nr");
|
|
for (reg = regs->first; reg != NULL; reg = reg->next, ++regnum)
|
|
gdb_printf (" %-11s %3d\n", reg->name, regnum);
|
|
}
|
|
|
|
void _initialize_user_regs ();
|
|
void
|
|
_initialize_user_regs ()
|
|
{
|
|
add_cmd ("user-registers", class_maintenance,
|
|
maintenance_print_user_registers,
|
|
_("List the names of the current user registers."),
|
|
&maintenanceprintlist);
|
|
}
|