Re: [RFC] Register sets

Daniel Jacobowitz
2003-08-24T16:43:51Z

On Sun, Aug 24, 2003 at 12:49:57AM +0200, Mark Kettenis wrote:
> Folks (and especially Andrew),
>
> A while back, Andrew submitted a patch that made it possible to read
> Linux/i386 corefiles on Linux/x86-64. He tricked me into fixing the
> "gcore" command for this case, which forced me to think a bit more
> about how we can solve this cleanly. Here's what I think is the
> solution.

Hi Mark,

I've been thinking about the same problem for a while now. I have a
slightly different mental picture; let's see if we can put them
together.

> Registers sets
> --------------
>
> In most hosted environments, the underlying OS devides the ISA's
> registers in groups. Typically there is a set of general-purpose
> (which usually includes all integer register)s and a set of
> floating-point registers. That's one of the reasons why we have
> reggroups in GDB.
>
> Most OS'es define data structures for dealing with these groups of
> registers:
>
> * Modern BSD's have `struct reg' and `struct fpreg'.
>
> * System V has `gregset_t' and `fpregset_t'. Solaris (as an
> SVR4-derivative) has `prgregset_t' and `fpregset_t' and the
> provision for an `xregset'.
>
> * QNX has `procfs_greg', `procfs_fpreg' and `procfs_altreg'.
>
> * Linux is a bit messy, but tries to mimic SVR4/Solaris.
>
> These data structures are used in several interfaces:
>
> * ptrace(2) (for systems that have PT_GETREGS/PTRACE_GETREGS and
> friends, e.g. modern BSD's and most Linuxen).
>
> * proc(4) filesystem (a.k.a. procfs on e.g. SVR4/Solaris and QNX).
>
> * libthread_db(3)/proc_service(3) debugging interface (on Solaris and
> Linux).
>
> * core(5) memory dumps (on ELF systems, but also in some cases,
> traditional a.out systems).
>
> As a result, we use these data structures all over the place in GDB,
> usually in ways that make it difficult to re-use code and/or
> multi-arch things. I'd like to introduce a more unified approach to
> handling these data structures, which I propose to call register sets,
> or regsets for short. Basically a register set is just a block of
> memory of a certain size. We may want to put these register sets (or
> a single register from them) into GDB's register cache. We also need
> to be able to grab these register sets (or a single register in them)
> from the register cache.
>
> It seems that we need a maximum of three of these sets. I propose to
> name these register sets as follows:
>
> * `gregset' for the general-purpose registers.
>
> * `fpregset' for the floating-point registers.
>
> * `xregset' for any "extra" registers.

I don't think this is a good assumption. There are two problems with
it:
- It assumes that everything relating to a particular target uses the
same register set format. In general (there are exceptions where
libthread_db will zero the regset instead of calling ps_*getregs) we
can pass regsets through libthread_db as opaque objects; we might wish
to include registers that are not available in a core dump. Then we've
got two different "general" regsets. There are some other examples.

- It assumes that there is one possible xregset for a target.
Suppose that you have two ARM targets with different vector extensions;
I'll pick Cirrus/Maverick and iWMMXt, since there happen to be two ARM
targets with different vector extensions. We may not know right away
which regset is available. I'm not sure this is a problem; we could
just define the architecture appropriately; but another example is the
embedded x86 architecture registers, which a remote stub might want to
transfer.

(Choice of iWMMXt isn't just an example. I need to add gdbserver
support for iWMMXt, and I want to do it without changing the g/G
packet. See below...)

> For each register set we have to provide a function that takes apart
> the register set and stores it in GDB's register cache, and a
> functions that puts together a register set from GDB's register set.
> For these functions I propose the functions:
>
> void <prefix>_supply_<regset> (struct regcache *regcache,
> const void *regs, int regnum);
>
> for supplying register REGNUM from register set REGS into the cache
> REGCACHE, and
>
> void <prefix>_collect_<regset> (const struct regcache *regcache,
> void *regs, int regnum)
>
> for collecting register REGNUM from the cache REGCACHE into register
> set REGS.
>
> If REGNUM is -1, these function operate on all registers within the set.

Can we define the REGNUM != -1 case a little more clearly? Is the
regnum a hint, what registers must be valid in the regcache when
collecting, what registers must be valid in the regset when supplying,
et cetera. Right now we're a bit inconsistent between targets.

> For each architecture we will have a structure that contains all
> information about the register sets:
>
> struct regset_info
> {
> size_t sizeof_gregset;
> void (*supply_gregset)(struct regcache *, const void *, int);
> void (*collect_gregset)(const struct regcache *, void *, int);
> size_t sizeof_fpregset;
> void (*supply_fpregset)(struct regcache *, const void *, int);
> void (*collect_fpregset)(const struct regcache *, void *, int);
> size_t sizeof_xregset;
> void (*supply_xregset)(const struct regcache *, void *, int);
> void (*collect_xregset)(const struct regcache *, void *, int);
> };
>
> A pointer to this structure will be stored in the architecture vector,
> such that we can use this from various places in GDB.
>
> Thoughts?

I was thinking of something like this, very roughly:

struct regset
{
size_t size;
mapping_of_registers_and_sizes_and_offsets mapping;
};

struct native_regsets
{
struct regset *gregset, *fpregset, *xregset;
};

struct regset *
gdbarch_core_section_to_regset (int which, int sizeof);

This would replace lots of identical copies of fetch_core_registers all
over GDB.

And then:

struct regset *
gdbarch_remote_name_to_regset (int which, int sizeof);

The companion to this would be some remote protocol work:
-> qRegsets::
<- qRegsets:General,FP,iWMMXt

[Local GDB calls gdbarch_remote_name_to_regset on each of these]

-> g
<- 0000102109210831973987etc

[This would be the General set.]

-> qFetchRegset:FP
<- 000000831092831098201923etc

-> qSetRegset:FP:100000831092831098201923etc
<- OK

[This would be the FP set.]

Also available might be:

-> qDescribeRegset:General
<- a0:8;a1:8;a2:8;a3:8;v0:8;sp:8;pc:8

which could be used before trying the gdbarch_remote_name_to_regset as
a fallback. And this logically extends to a CLI command to specify the
format of the G packet, which I think there's a PR requesting?

How does that sound? I'll implement it if folks like it. Open to
any/all suggestions.

--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer