[PATCH 01/22] [GDBserver] Multi-process + multi-arch: core + GNU/Linux x86*

Pedro Alves
2013-05-30T12:13:53Z

This patch makes GDBserver support multi-process + biarch.

Currently, if you're debugging more than one process at once with a
single gdbserver (in extended-remote mode), then all processes must
have the same architecture (e.g., 64-bit vs 32-bit). Otherwise, you
see this:

Added inferior 2
[Switching to inferior 2 [<null>] (<noexec>)]
Reading symbols from /home/pedro/gdb/tests/main32...done.
Temporary breakpoint 2 at 0x4004cf: main. (2 locations)
Starting program: /home/pedro/gdb/tests/main32
warning: Selected architecture i386 is not compatible with reported target architecture i386:x86-64
warning: Architecture rejected target-supplied description
Remote 'g' packet reply is too long: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090cfffff0000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000b042f7460000000000020000230000002b0000002b0000002b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f03000000000000ffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000!
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f00003b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
... etc, etc ...

Even though the process was running a 32-bit program, GDBserver sent
back to GDB a register set in 64-bit layout.

A patch (http://sourceware.org/ml/gdb-patches/2012-11/msg00228.html) a
while ago made GDB track a target_gdbarch per inferior, and as
consequence, fetch a target description per-inferior. This patch is
the GDBserver counterpart, that makes GDBserver keep track of each
process'es XML target description and register layout. So in the
example above, GDBserver will send the correct register set in 32-bit
layout to GDB.

A new "struct target_desc" object (tdesc for short) is added, that
holds the target description and register layout information about
each process. Each `struct process_info' holds a pointer to a target
description. The regcache also gains a pointer to a target
description, mainly for convenience, and parallel with GDB (and
possible future support for programs that flip processor modes).

The low target's arch_setup routines are responsible for setting the
process'es correct tdesc. This isn't that much different to how
things were done before, except that instead of detecting the inferior
process'es architecture and calling the corresponding
init_registers_FOO routine, which would change the regcache layout
globals and recreate the threads' regcaches, the regcache.c globals
are gone, and the init_registers_$BAR routines now each initialize a
separate global struct target_desc object (one for each arch variant
GDBserver supports), and so all the init_registers_$BAR routines that
are built into GDBserver are called early at GDBserver startup time
(similarly to how GDB handles its built-in target descriptions), and
then the arch_setup routine is responsible for making
process_info->tdesc point to one of these target description globals.
The regcache module is all parameterized to get the regcache's layout
from the tdesc object instead of the old register_bytes, etc. globals.

The threads' regcaches are now created lazilly. The old scheme where
we created each of them when we added a new thread doesn't work
anymore, because we add the main thread/lwp before we see it stop for
the first time, and it is only when we see the thread stop for the
first time that we have a chance of determining the inferior's
architecture (through the_low_target.arch_setup). Therefore when we
add the main thread we don't know which architecture/tdesc its
regcache should have.

This patch makes the gdb.multi/multi-arch.exp test now pass against
(extended-remote) GDBserver. It currently fails, without this patch.

I simply replaced the i387-fp.c:num_xmm_registers global with a check
for 64-bit or 32-bit process, which is equivalent to how the global
was set. This avoided coming up with some more general mechanism that
would work for all targets that use this module (GNU/Linux, Windows,
etc.).

The IPA also uses the regcache, so it gains a new global struct
target_desc pointer, which points at the description of the process it
is loaded in.

Re. the linux-low.c & friends changes. Since the register map
etc. may differ between processes (64-bit vs 32-bit) etc., the
linux_target_ops num_regs, regmap and regset_bitmap data fields are no
longer sufficient. A new method is added in their place that returns
a pointer to a new struct that includes all info linux-low.c needs to
access registers of the current inferior.

The patch/discussion that originally introduced
linux-low.c:disabled_regsets mentions that the disabled_regsets set
may be different per mode (in a biarch setup), and indeed that is
cleared whenever we start a new (first) inferior, so that global is
moved as well behind the new `struct regs_info'.

Tested x86_64 Fedora 17, -m64 and -m32.

gdb/gdbserver/
2013-05-30 Pedro Alves <palves@redhat.com>

* Makefile.in (OBS): Add tdesc.o.
(IPA_OBJS): Add tdesc-ipa.o.
(tdesc-ipa.o): New rule.
* ax.c (gdb_eval_agent_expr): Adjust register_size call to new
interface.
* i387-fp.c (num_xmm_registers): Delete.
(i387_cache_to_fsave, i387_fsave_to_cache): Adjust find_regno
calls to new interface.
(i387_cache_to_fxsave, i387_cache_to_xsave, i387_fxsave_to_cache)
(i387_xsave_to_cache): Adjust find_regno calls to new interface.
Infer the number of xmm registers from the regcache's target
description.
* i387-fp.h (num_xmm_registers): Delete.
* inferiors.c (add_thread): Don't install the thread's regcache
here.
* linux-amd64-ipa.c (tdesc_amd64_linux): Declare.
(gdbserver_xmltarget): Delete.
(initialize_low_tracepoint): Set the ipa's target description.
* linux-i386-ipa.c (tdesc_i386_linux): Declare.
(initialize_low_tracepoint): Set the ipa's target description.
* linux-low.c (new_inferior): Delete.
(disabled_regsets, num_regsets): Delete.
(linux_add_process): Adjust to set the new per-process
new_inferior flag.
(linux_detach_one_lwp): Adjust to call regcache_invalidate_thread.
(linux_wait_for_lwp): Adjust. Only call arch_setup if the event
was a stop. When calling arch_setup, switch the current inferior
to the thread that got an event.
(linux_resume_one_lwp): Adjust to call regcache_invalidate_thread.
(regsets_fetch_inferior_registers)
(regsets_store_inferior_registers): New regsets_info parameter.
Adjust to use it.
(linux_register_in_regsets): New regs_info parameter. Adjust to
use it.
(register_addr, fetch_register, store_register): New usrregs_info
parameter. Adjust to use it.
(usr_fetch_inferior_registers, usr_store_inferior_registers): New
parameter regs_info. Adjust to use it.
(linux_fetch_registers): Get the current inferior's regs_info, and
adjust to use it.
(linux_store_registers): Ditto.
[HAVE_LINUX_REGSETS] (initialize_regsets_info): New.
(initialize_low): Don't initialize the target_regsets here. Call
initialize_low_arch.
* linux-low.h (target_regsets): Delete declaration.
(struct regsets_info): New.
(struct usrregs_info): New.
(struct regs_info): New.
(struct process_info_private) <new_inferior>: New field.
(struct linux_target_ops): Delete the num_regs, regmap, and
regset_bitmap fields. New field regs_info.
[HAVE_LINUX_REGSETS] (initialize_regsets_info): Declare.
* linux-x86-low.c: Include tdesc.h.
[__x86_64__] (is_64bit_tdesc): New.
(ps_get_thread_area, x86_get_thread_area): Use it.
(i386_cannot_store_register): Rename to ...
(x86_cannot_store_register): ... this. Use is_64bit_tdesc.
(i386_cannot_fetch_register): Rename to ...
(x86_cannot_fetch_register): ... this. Use is_64bit_tdesc.
(x86_fill_gregset, x86_store_gregset): Adjust register_size calls
to new interface.
(target_regsets): Rename to ...
(x86_regsets): ... this.
(x86_get_pc, x86_set_pc): Adjust register_size calls to new
interface.
(x86_siginfo_fixup): Use is_64bit_tdesc.
[__x86_64__] (tdesc_amd64_linux, tdesc_amd64_avx_linux)
(tdesc_x32_avx_linux, tdesc_x32_linux)
(tdesc_i386_linux, tdesc_i386_mmx_linux, tdesc_i386_avx_linux):
Declare.
(x86_linux_update_xmltarget): Delete.
(I386_LINUX_XSAVE_XCR0_OFFSET): Define.
(have_ptrace_getfpxregs, have_ptrace_getregset): New.
(AMD64_LINUX_USER64_CS): New.
(x86_linux_read_description): New, based on
x86_linux_update_xmltarget.
(same_process_callback): New.
(x86_arch_setup_process_callback): New.
(x86_linux_update_xmltarget): New.
(x86_regsets_info): New.
(amd64_linux_regs_info): New.
(i386_linux_usrregs_info): New.
(i386_linux_regs_info): New.
(x86_linux_regs_info): New.
(x86_arch_setup): Reimplement.
(x86_install_fast_tracepoint_jump_pad): Use is_64bit_tdesc.
(x86_emit_ops): Ditto.
(the_low_target): Adjust. Install x86_linux_regs_info,
x86_cannot_fetch_register, and x86_cannot_store_register.
(initialize_low_arch): New.
* proc-service.c (gregset_info): Fetch the current inferior's
regs_info. Adjust to use it.
* regcache.c: Include tdesc.h.
(register_bytes, reg_defs, num_registers)
(gdbserver_expedite_regs): Delete.
(get_thread_regcache): If the thread doesn't have a regcache yet,
create one, instead of aborting gdbserver.
(regcache_invalidate_one): Rename to ...
(regcache_invalidate_thread): ... this.
(regcache_invalidate_one): New.
(regcache_invalidate): Only invalidate registers of the current
process.
(init_register_cache): Add target_desc parameter, and use it.
(new_register_cache): Ditto. Assert the target description has a
non zero registers_size.
(regcache_cpy): Add assertions. Adjust.
(realloc_register_cache, set_register_cache): Delete.
(registers_to_string, registers_from_string): Adjust.
(find_register_by_name, find_regno, find_register_by_number)
(register_cache_size): Add target_desc parameter, and use it.
(free_register_cache_thread, free_register_cache_thread_one)
(regcache_release, register_cache_size): New.
(register_size): Add target_desc parameter, and use it.
(register_data, supply_register, supply_register_zeroed)
(supply_regblock, supply_register_by_name, collect_register)
(collect_register_as_string, collect_register_by_name): Adjust.
* regcache.h (struct target_desc): Forward declare.
(struct regcache) <tdesc>: New field.
(init_register_cache, new_register_cache): Add target_desc
parameter.
(regcache_invalidate_thread): Declare.
(regcache_invalidate_one): Delete declaration.
(regcache_release): Declare.
(find_register_by_number, register_cache_size, register_size)
(find_regno): Add target_desc parameter.
(gdbserver_expedite_regs, gdbserver_xmltarget): Delete
declarations.
* remote-utils.c: Include tdesc.h.
(outreg, prepare_resume_reply): Adjust.
* server.c: Include tdesc.h.
(gdbserver_xmltarget): Delete declaration.
(get_features_xml, process_serial_event): Adjust.
* server.h [IN_PROCESS_AGENT] (struct target_desc): Forward
declare.
(struct process_info) <tdesc>: New field.
(ipa_tdesc): Declare.
* tdesc.c: New file.
* tdesc.h: New file.
* tracepoint.c: Include tdesc.h.
[IN_PROCESS_AGENT] (ipa_tdesc): Define.
(get_context_regcache): Adjust to pass ipa_tdesc down.
(do_action_at_tracepoint): Adjust to get the register cache size
from the context regcache's description.
(traceframe_walk_blocks): Adjust to get the register cache size
from the current trace frame's description.
(traceframe_get_pc): Adjust to get current trace frame's
description and pass it down.
(gdb_collect): Adjust to get the register cache size from the
IPA's description.

gdb/
2013-05-30 Pedro Alves <palves@redhat.com>

* regformats/regdat.sh: Output #include tdesc.h. Make globals
static. Output a global target description pointer.
(init_registers_${name}): Adjust to initialize a
target description structure.
---
gdb/gdbserver/Makefile.in | 7 -
gdb/gdbserver/ax.c | 2
gdb/gdbserver/i387-fp.c | 34 ++-
gdb/gdbserver/i387-fp.h | 2
gdb/gdbserver/inferiors.c | 1
gdb/gdbserver/linux-amd64-ipa.c | 6 -
gdb/gdbserver/linux-i386-ipa.c | 2
gdb/gdbserver/linux-low.c | 217 +++++++++++-------
gdb/gdbserver/linux-low.h | 72 +++++-
gdb/gdbserver/linux-x86-low.c | 467 +++++++++++++++++++++++++--------------
gdb/gdbserver/proc-service.c | 10 +
gdb/gdbserver/regcache.c | 235 +++++++++++---------
gdb/gdbserver/regcache.h | 33 ++-
gdb/gdbserver/remote-utils.c | 9 -
gdb/gdbserver/server.c | 25 +-
gdb/gdbserver/server.h | 6 +
gdb/gdbserver/tdesc.c | 66 ++++++
gdb/gdbserver/tdesc.h | 64 +++++
gdb/gdbserver/tracepoint.c | 35 ++-
19 files changed, 849 insertions(+), 444 deletions(-)
create mode 100644 gdb/gdbserver/tdesc.c
create mode 100644 gdb/gdbserver/tdesc.h

diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index c7ff4c6..e8470a8 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -170,7 +170,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o targ
utils.o version.o vec.o gdb_vecs.o \
mem-break.o hostio.o event-loop.o tracepoint.o \
xml-utils.o common-utils.o ptid.o buffer.o format.o filestuff.o \
- dll.o notif.o \
+ dll.o notif.o tdesc.o \
$(XML_BUILTIN) \
$(DEPFILES) $(LIBOBJS)
GDBREPLAY_OBS = gdbreplay.o version.o
@@ -287,7 +287,7 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS)
${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \
$(XM_CLIBS)

-IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
+IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o tdesc-ipa.o ${IPA_DEPFILES}

IPA_LIB=libinproctrace.so

@@ -505,6 +505,9 @@ linux-amd64-ipa.o: linux-amd64-ipa.c
amd64-linux-ipa.o: amd64-linux.c
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
+tdesc-ipa.o: tdesc.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)

ax.o: ax.c
$(COMPILE) $(WARN_CFLAGS_NO_FORMAT) $<
diff --git a/gdb/gdbserver/ax.c b/gdb/gdbserver/ax.c
index cd5cf2b..b6824a2 100644
--- a/gdb/gdbserver/ax.c
+++ b/gdb/gdbserver/ax.c
@@ -1162,7 +1162,7 @@ gdb_eval_agent_expr (struct eval_agent_expr_context *ctx,
int regnum = arg;
struct regcache *regcache = ctx->regcache;

- switch (register_size (regnum))
+ switch (register_size (regcache->tdesc, regnum))
{
case 8:
collect_register (regcache, regnum, cnv.u64.bytes);
diff --git a/gdb/gdbserver/i387-fp.c b/gdb/gdbserver/i387-fp.c
index d2543b2..2886519 100644
--- a/gdb/gdbserver/i387-fp.c
+++ b/gdb/gdbserver/i387-fp.c
@@ -20,8 +20,6 @@
#include "i387-fp.h"
#include "i386-xstate.h"

-int num_xmm_registers = 8;
-
/* Note: These functions preserve the reserved bits in control registers.
However, gdbserver promptly throws away that information. */

@@ -117,7 +115,7 @@ i387_cache_to_fsave (struct regcache *regcache, void *buf)
{
struct i387_fsave *fp = (struct i387_fsave *) buf;
int i;
- int st0_regnum = find_regno ("st0");
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
unsigned long val, val2;

for (i = 0; i < 8; i++)
@@ -157,7 +155,7 @@ i387_fsave_to_cache (struct regcache *regcache, const void *buf)
{
struct i387_fsave *fp = (struct i387_fsave *) buf;
int i;
- int st0_regnum = find_regno ("st0");
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
unsigned long val;

for (i = 0; i < 8; i++)
@@ -193,9 +191,11 @@ i387_cache_to_fxsave (struct regcache *regcache, void *buf)
{
struct i387_fxsave *fp = (struct i387_fxsave *) buf;
int i;
- int st0_regnum = find_regno ("st0");
- int xmm0_regnum = find_regno ("xmm0");
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
unsigned long val, val2;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;

for (i = 0; i < 8; i++)
collect_register (regcache, i + st0_regnum,
@@ -249,6 +249,8 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
unsigned long long xstate_bv = 0;
char raw[16];
char *p;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;

/* The supported bits in `xstat_bv' are 1 byte. Clear part in
vector registers if its bit in xstat_bv is zero. */
@@ -274,7 +276,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
/* Check if any x87 registers are changed. */
if ((x86_xcr0 & I386_XSTATE_X87))
{
- int st0_regnum = find_regno ("st0");
+ int st0_regnum = find_regno (regcache->tdesc, "st0");

for (i = 0; i < 8; i++)
{
@@ -291,7 +293,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
/* Check if any SSE registers are changed. */
if ((x86_xcr0 & I386_XSTATE_SSE))
{
- int xmm0_regnum = find_regno ("xmm0");
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");

for (i = 0; i < num_xmm_registers; i++)
{
@@ -308,7 +310,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
/* Check if any AVX registers are changed. */
if ((x86_xcr0 & I386_XSTATE_AVX))
{
- int ymm0h_regnum = find_regno ("ymm0h");
+ int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");

for (i = 0; i < num_xmm_registers; i++)
{
@@ -413,9 +415,11 @@ i387_fxsave_to_cache (struct regcache *regcache, const void *buf)
{
struct i387_fxsave *fp = (struct i387_fxsave *) buf;
int i, top;
- int st0_regnum = find_regno ("st0");
- int xmm0_regnum = find_regno ("xmm0");
+ int st0_regnum = find_regno (regcache->tdesc, "st0");
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
unsigned long val;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;

for (i = 0; i < 8; i++)
supply_register (regcache, i + st0_regnum,
@@ -468,6 +472,8 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
unsigned long val;
unsigned int clear_bv;
gdb_byte *p;
+ /* Amd64 has 16 xmm regs; I386 has 8 xmm regs. */
+ int num_xmm_registers = register_size (regcache->tdesc, 0) == 8 ? 16 : 8;

/* The supported bits in `xstat_bv' are 1 byte. Clear part in
vector registers if its bit in xstat_bv is zero. */
@@ -476,7 +482,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
/* Check if any x87 registers are changed. */
if ((x86_xcr0 & I386_XSTATE_X87) != 0)
{
- int st0_regnum = find_regno ("st0");
+ int st0_regnum = find_regno (regcache->tdesc, "st0");

if ((clear_bv & I386_XSTATE_X87) != 0)
{
@@ -493,7 +499,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)

if ((x86_xcr0 & I386_XSTATE_SSE) != 0)
{
- int xmm0_regnum = find_regno ("xmm0");
+ int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");

if ((clear_bv & I386_XSTATE_SSE))
{
@@ -510,7 +516,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)

if ((x86_xcr0 & I386_XSTATE_AVX) != 0)
{
- int ymm0h_regnum = find_regno ("ymm0h");
+ int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");

if ((clear_bv & I386_XSTATE_AVX) != 0)
{
diff --git a/gdb/gdbserver/i387-fp.h b/gdb/gdbserver/i387-fp.h
index efc70ed..9473cda 100644
--- a/gdb/gdbserver/i387-fp.h
+++ b/gdb/gdbserver/i387-fp.h
@@ -30,6 +30,4 @@ void i387_xsave_to_cache (struct regcache *regcache, const void *buf);

extern unsigned long long x86_xcr0;

-extern int num_xmm_registers;
-
#endif /* I387_FP_H */
diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c
index 6953d0e..39eb52e 100644
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -102,7 +102,6 @@ add_thread (ptid_t thread_id, void *target_data)
current_inferior = new_thread;

new_thread->target_data = target_data;
- set_inferior_regcache_data (new_thread, new_register_cache ());
}

ptid_t
diff --git a/gdb/gdbserver/linux-amd64-ipa.c b/gdb/gdbserver/linux-amd64-ipa.c
index dc20a15..34daedf 100644
--- a/gdb/gdbserver/linux-amd64-ipa.c
+++ b/gdb/gdbserver/linux-amd64-ipa.c
@@ -22,6 +22,7 @@

/* Defined in auto-generated file amd64-linux.c. */
void init_registers_amd64_linux (void);
+extern const struct target_desc *tdesc_amd64_linux;

/* fast tracepoints collect registers. */

@@ -164,12 +165,9 @@ supply_static_tracepoint_registers (struct regcache *regcache,

#endif /* HAVE_UST */

-/* This is only needed because reg-i386-linux-lib.o references it. We
- may use it proper at some point. */
-const char *gdbserver_xmltarget;
-
void
initialize_low_tracepoint (void)
{
init_registers_amd64_linux ();
+ ipa_tdesc = tdesc_amd64_linux;
}
diff --git a/gdb/gdbserver/linux-i386-ipa.c b/gdb/gdbserver/linux-i386-ipa.c
index 1c52284..e0245c5 100644
--- a/gdb/gdbserver/linux-i386-ipa.c
+++ b/gdb/gdbserver/linux-i386-ipa.c
@@ -49,6 +49,7 @@ enum i386_gdb_regnum

/* Defined in auto-generated file i386-linux.c. */
void init_registers_i386_linux (void);
+extern const struct target_desc *tdesc_i386_linux;

#define FT_CR_EAX 15
#define FT_CR_ECX 14
@@ -250,5 +251,6 @@ void
initialize_low_tracepoint (void)
{
init_registers_i386_linux ();
+ ipa_tdesc = tdesc_i386_linux;
initialize_fast_tracepoint_trampoline_buffer ();
}
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 7e18942..660a6a2 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -224,15 +224,6 @@ int using_threads = 1;
jump pads). */
static int stabilizing_threads;

-/* This flag is true iff we've just created or attached to our first
- inferior but it has not stopped yet. As soon as it does, we need
- to call the low target's arch_setup callback. Doing this only on
- the first inferior avoids reinializing the architecture on every
- inferior, and avoids messing with the register caches of the
- already running inferiors. NOTE: this assumes all inferiors under
- control of gdbserver have the same architecture. */
-static int new_inferior;
-
static void linux_resume_one_lwp (struct lwp_info *lwp,
int step, int signal, siginfo_t *info);
static void linux_resume (struct thread_resume *resume_info, size_t n);
@@ -293,11 +284,6 @@ struct pending_signals
struct pending_signals *prev;
};

-#ifdef HAVE_LINUX_REGSETS
-static char *disabled_regsets;
-static int num_regsets;
-#endif
-
/* The read/write ends of the pipe registered as waitable file in the
event loop. */
static int linux_event_pipe[2] = { -1, -1 };
@@ -379,13 +365,12 @@ linux_add_process (int pid, int attached)
{
struct process_info *proc;

- /* Is this the first process? If so, then set the arch. */
- if (all_processes.head == NULL)
- new_inferior = 1;
-
proc = add_process (pid, attached);
proc->private = xcalloc (1, sizeof (*proc->private));

+ /* Set the arch when the first LWP stops. */
+ proc->private->new_inferior = 1;
+
if (the_low_target.new_process != NULL)
proc->private->arch_private = the_low_target.new_process ();

@@ -1203,8 +1188,7 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args)
}

/* Flush any pending changes to the process's registers. */
- regcache_invalidate_one ((struct inferior_list_entry *)
- get_lwp_thread (lwp));
+ regcache_invalidate_thread (get_lwp_thread (lwp));

/* Pass on any pending signal for this thread. */
sig = get_detach_signal (thread);
@@ -1412,17 +1396,28 @@ retry:

child->last_status = *wstatp;

- /* Architecture-specific setup after inferior is running.
- This needs to happen after we have attached to the inferior
- and it is stopped for the first time, but before we access
- any inferior registers. */
- if (new_inferior)
+ if (WIFSTOPPED (*wstatp))
{
- the_low_target.arch_setup ();
-#ifdef HAVE_LINUX_REGSETS
- memset (disabled_regsets, 0, num_regsets);
-#endif
- new_inferior = 0;
+ struct process_info *proc;
+
+ /* Architecture-specific setup after inferior is running. This
+ needs to happen after we have attached to the inferior and it
+ is stopped for the first time, but before we access any
+ inferior registers. */
+ proc = find_process_pid (pid_of (child));
+ if (proc->private->new_inferior)
+ {
+ struct thread_info *saved_inferior;
+
+ saved_inferior = current_inferior;
+ current_inferior = get_lwp_thread (child);
+
+ the_low_target.arch_setup ();
+
+ current_inferior = saved_inferior;
+
+ proc->private->new_inferior = 0;
+ }
}

/* Fetch the possibly triggered data watchpoint info and store it in
@@ -3348,8 +3343,7 @@ lwp %ld wants to get out of fast tracepoint jump pad single-stepping\n",
if (the_low_target.prepare_to_resume != NULL)
the_low_target.prepare_to_resume (lwp);

- regcache_invalidate_one ((struct inferior_list_entry *)
- get_lwp_thread (lwp));
+ regcache_invalidate_thread (get_lwp_thread (lwp));
errno = 0;
lwp->stopped = 0;
lwp->stopped_by_watchpoint = 0;
@@ -4058,14 +4052,15 @@ unstop_all_lwps (int unsuspend, struct lwp_info *except)
#define use_linux_regsets 1

static int
-regsets_fetch_inferior_registers (struct regcache *regcache)
+regsets_fetch_inferior_registers (struct regsets_info *regsets_info,
+ struct regcache *regcache)
{
struct regset_info *regset;
int saw_general_regs = 0;
int pid;
struct iovec iov;

- regset = target_regsets;
+ regset = regsets_info->regsets;

pid = lwpid_of (get_thread_lwp (current_inferior));
while (regset->size >= 0)
@@ -4073,7 +4068,8 @@ regsets_fetch_inferior_registers (struct regcache *regcache)
void *buf, *data;
int nt_type, res;

- if (regset->size == 0 || disabled_regsets[regset - target_regsets])
+ if (regset->size == 0
+ || regsets_info->disabled_regsets[regset - regsets_info->regsets])
{
regset ++;
continue;
@@ -4101,9 +4097,12 @@ regsets_fetch_inferior_registers (struct regcache *regcache)
{
if (errno == EIO)
{
+ int dr_offset;
+
/* If we get EIO on a regset, do not try it again for
- this process. */
- disabled_regsets[regset - target_regsets] = 1;
+ this process mode. */
+ dr_offset = regset - regsets_info->regsets;
+ regsets_info->disabled_regsets[dr_offset] = 1;
free (buf);
continue;
}
@@ -4128,14 +4127,15 @@ regsets_fetch_inferior_registers (struct regcache *regcache)
}

static int
-regsets_store_inferior_registers (struct regcache *regcache)
+regsets_store_inferior_registers (struct regsets_info *regsets_info,
+ struct regcache *regcache)
{
struct regset_info *regset;
int saw_general_regs = 0;
int pid;
struct iovec iov;

- regset = target_regsets;
+ regset = regsets_info->regsets;

pid = lwpid_of (get_thread_lwp (current_inferior));
while (regset->size >= 0)
@@ -4143,7 +4143,8 @@ regsets_store_inferior_registers (struct regcache *regcache)
void *buf, *data;
int nt_type, res;

- if (regset->size == 0 || disabled_regsets[regset - target_regsets])
+ if (regset->size == 0
+ || regsets_info->disabled_regsets[regset - regsets_info->regsets])
{
regset ++;
continue;
@@ -4190,9 +4191,12 @@ regsets_store_inferior_registers (struct regcache *regcache)
{
if (errno == EIO)
{
+ int dr_offset;
+
/* If we get EIO on a regset, do not try it again for
- this process. */
- disabled_regsets[regset - target_regsets] = 1;
+ this process mode. */
+ dr_offset = regset - regsets_info->regsets;
+ regsets_info->disabled_regsets[dr_offset] = 1;
free (buf);
continue;
}
@@ -4224,8 +4228,8 @@ regsets_store_inferior_registers (struct regcache *regcache)
#else /* !HAVE_LINUX_REGSETS */

#define use_linux_regsets 0
-#define regsets_fetch_inferior_registers(regcache) 1
-#define regsets_store_inferior_registers(regcache) 1
+#define regsets_fetch_inferior_registers(regsets_info, regcache) 1
+#define regsets_store_inferior_registers(regsets_info, regcache) 1

#endif

@@ -4233,50 +4237,52 @@ regsets_store_inferior_registers (struct regcache *regcache)
calls or 0 if it has to be transferred individually. */

static int
-linux_register_in_regsets (int regno)
+linux_register_in_regsets (const struct regs_info *regs_info, int regno)
{
unsigned char mask = 1 << (regno % 8);
size_t index = regno / 8;

return (use_linux_regsets
- && (the_low_target.regset_bitmap == NULL
- || (the_low_target.regset_bitmap[index] & mask) != 0));
+ && (regs_info->regset_bitmap == NULL
+ || (regs_info->regset_bitmap[index] & mask) != 0));
}

#ifdef HAVE_LINUX_USRREGS

int
-register_addr (int regnum)
+register_addr (const struct usrregs_info *usrregs, int regnum)
{
int addr;

- if (regnum < 0 || regnum >= the_low_target.num_regs)
+ if (regnum < 0 || regnum >= usrregs->num_regs)
error ("Invalid register number %d.", regnum);

- addr = the_low_target.regmap[regnum];
+ addr = usrregs->regmap[regnum];

return addr;
}

/* Fetch one register. */
static void
-fetch_register (struct regcache *regcache, int regno)
+fetch_register (const struct usrregs_info *usrregs,
+ struct regcache *regcache, int regno)
{
CORE_ADDR regaddr;
int i, size;
char *buf;
int pid;

- if (regno >= the_low_target.num_regs)
+ if (regno >= usrregs->num_regs)
return;
if ((*the_low_target.cannot_fetch_register) (regno))
return;

- regaddr = register_addr (regno);
+ regaddr = register_addr (usrregs, regno);
if (regaddr == -1)
return;

- size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ size = ((register_size (regcache->tdesc, regno)
+ + sizeof (PTRACE_XFER_TYPE) - 1)
& -sizeof (PTRACE_XFER_TYPE));
buf = alloca (size);

@@ -4302,23 +4308,25 @@ fetch_register (struct regcache *regcache, int regno)

/* Store one register. */
static void
-store_register (struct regcache *regcache, int regno)
+store_register (const struct usrregs_info *usrregs,
+ struct regcache *regcache, int regno)
{
CORE_ADDR regaddr;
int i, size;
char *buf;
int pid;

- if (regno >= the_low_target.num_regs)
+ if (regno >= usrregs->num_regs)
return;
if ((*the_low_target.cannot_store_register) (regno))
return;

- regaddr = register_addr (regno);
+ regaddr = register_addr (usrregs, regno);
if (regaddr == -1)
return;

- size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ size = ((register_size (regcache->tdesc, regno)
+ + sizeof (PTRACE_XFER_TYPE) - 1)
& -sizeof (PTRACE_XFER_TYPE));
buf = alloca (size);
memset (buf, 0, size);
@@ -4359,16 +4367,19 @@ store_register (struct regcache *regcache, int regno)
unless ALL is non-zero.
Otherwise, REGNO specifies which register (so we can save time). */
static void
-usr_fetch_inferior_registers (struct regcache *regcache, int regno, int all)
+usr_fetch_inferior_registers (const struct regs_info *regs_info,
+ struct regcache *regcache, int regno, int all)
{
+ struct usrregs_info *usr = regs_info->usrregs;
+
if (regno == -1)
{
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- if (all || !linux_register_in_regsets (regno))
- fetch_register (regcache, regno);
+ for (regno = 0; regno < usr->num_regs; regno++)
+ if (all || !linux_register_in_regsets (regs_info, regno))
+ fetch_register (usr, regcache, regno);
}
else
- fetch_register (regcache, regno);
+ fetch_register (usr, regcache, regno);
}

/* Store our register values back into the inferior.
@@ -4377,22 +4388,25 @@ usr_fetch_inferior_registers (struct regcache *regcache, int regno, int all)
unless ALL is non-zero.
Otherwise, REGNO specifies which register (so we can save time). */
static void
-usr_store_inferior_registers (struct regcache *regcache, int regno, int all)
+usr_store_inferior_registers (const struct regs_info *regs_info,
+ struct regcache *regcache, int regno, int all)
{
+ struct usrregs_info *usr = regs_info->usrregs;
+
if (regno == -1)
{
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- if (all || !linux_register_in_regsets (regno))
- store_register (regcache, regno);
+ for (regno = 0; regno < usr->num_regs; regno++)
+ if (all || !linux_register_in_regsets (regs_info, regno))
+ store_register (usr, regcache, regno);
}
else
- store_register (regcache, regno);
+ store_register (usr, regcache, regno);
}

#else /* !HAVE_LINUX_USRREGS */

-#define usr_fetch_inferior_registers(regcache, regno, all) do {} while (0)
-#define usr_store_inferior_registers(regcache, regno, all) do {} while (0)
+#define usr_fetch_inferior_registers(regs_info, regcache, regno, all) do {} while (0)
+#define usr_store_inferior_registers(regs_info, regcache, regno, all) do {} while (0)

#endif

@@ -4402,15 +4416,18 @@ linux_fetch_registers (struct regcache *regcache, int regno)
{
int use_regsets;
int all = 0;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();

if (regno == -1)
{
- if (the_low_target.fetch_register != NULL)
- for (regno = 0; regno < the_low_target.num_regs; regno++)
+ if (the_low_target.fetch_register != NULL
+ && regs_info->usrregs != NULL)
+ for (regno = 0; regno < regs_info->usrregs->num_regs; regno++)
(*the_low_target.fetch_register) (regcache, regno);

- all = regsets_fetch_inferior_registers (regcache);
- usr_fetch_inferior_registers (regcache, -1, all);
+ all = regsets_fetch_inferior_registers (regs_info->regsets_info, regcache);
+ if (regs_info->usrregs != NULL)
+ usr_fetch_inferior_registers (regs_info, regcache, -1, all);
}
else
{
@@ -4418,11 +4435,12 @@ linux_fetch_registers (struct regcache *regcache, int regno)
&& (*the_low_target.fetch_register) (regcache, regno))
return;

- use_regsets = linux_register_in_regsets (regno);
+ use_regsets = linux_register_in_regsets (regs_info, regno);
if (use_regsets)
- all = regsets_fetch_inferior_registers (regcache);
- if (!use_regsets || all)
- usr_fetch_inferior_registers (regcache, regno, 1);
+ all = regsets_fetch_inferior_registers (regs_info->regsets_info,
+ regcache);
+ if ((!use_regsets || all) && regs_info->usrregs != NULL)
+ usr_fetch_inferior_registers (regs_info, regcache, regno, 1);
}
}

@@ -4431,19 +4449,23 @@ linux_store_registers (struct regcache *regcache, int regno)
{
int use_regsets;
int all = 0;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();

if (regno == -1)
{
- all = regsets_store_inferior_registers (regcache);
- usr_store_inferior_registers (regcache, regno, all);
+ all = regsets_store_inferior_registers (regs_info->regsets_info,
+ regcache);
+ if (regs_info->usrregs != NULL)
+ usr_store_inferior_registers (regs_info, regcache, regno, all);
}
else
{
- use_regsets = linux_register_in_regsets (regno);
+ use_regsets = linux_register_in_regsets (regs_info, regno);
if (use_regsets)
- all = regsets_store_inferior_registers (regcache);
- if (!use_regsets || all)
- usr_store_inferior_registers (regcache, regno, 1);
+ all = regsets_store_inferior_registers (regs_info->regsets_info,
+ regcache);
+ if ((!use_regsets || all) && regs_info->usrregs != NULL)
+ usr_store_inferior_registers (regs_info, regcache, regno, 1);
}
}

@@ -5884,8 +5906,14 @@ linux_low_enable_btrace (ptid_t ptid)
struct btrace_target_info *tinfo;

tinfo = linux_enable_btrace (ptid);
+
if (tinfo != NULL)
- tinfo->ptr_bits = register_size (0) * 8;
+ {
+ struct thread_info *thread = find_thread_ptid (ptid);
+ struct regcache *regcache = get_thread_regcache (thread, 0);
+
+ tinfo->ptr_bits = register_size (regcache->tdesc, 0) * 8;
+ }

return tinfo;
}
@@ -6005,6 +6033,18 @@ linux_init_signals ()
#endif
}

+#ifdef HAVE_LINUX_REGSETS
+void
+initialize_regsets_info (struct regsets_info *info)
+{
+ for (info->num_regsets = 0;
+ info->regsets[info->num_regsets].size >= 0;
+ info->num_regsets++)
+ ;
+ info->disabled_regsets = xmalloc (info->num_regsets);
+}
+#endif
+
void
initialize_low (void)
{
@@ -6016,14 +6056,11 @@ initialize_low (void)
linux_init_signals ();
linux_test_for_tracefork ();
linux_ptrace_init_warnings ();
-#ifdef HAVE_LINUX_REGSETS
- for (num_regsets = 0; target_regsets[num_regsets].size >= 0; num_regsets++)
- ;
- disabled_regsets = xmalloc (num_regsets);
-#endif

sigchld_action.sa_handler = sigchld_handler;
sigemptyset (&sigchld_action.sa_mask);
sigchld_action.sa_flags = SA_RESTART;
sigaction (SIGCHLD, &sigchld_action, NULL);
+
+ initialize_low_arch ();
}
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 4dd3c9c..bce0288 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -46,8 +46,58 @@ struct regset_info
regset_fill_func fill_function;
regset_store_func store_function;
};
-extern struct regset_info target_regsets[];
+
+/* Aggregation of all the supported regsets of a given
+ architecture/mode. */
+
+struct regsets_info
+{
+ /* The regsets array. */
+ struct regset_info *regsets;
+
+ /* The number of regsets in the REGSETS array. */
+ int num_regsets;
+
+ /* If we get EIO on a regset, do not try it again. Note the set of
+ supported regsets may depend on processor mode on biarch
+ machines. */
+ char *disabled_regsets;
+};
+
+#endif
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register array layout. */
+
+struct usrregs_info
+{
+ /* The number of registers accessible. */
+ int num_regs;
+
+ /* The registers map. */
+ int *regmap;
+};
+
+/* All info needed to access an architecture/mode's registers. */
+
+struct regs_info
+{
+ /* Regset support bitmap: 1 for registers that are transferred as a part
+ of a regset, 0 for ones that need to be handled individually. This
+ can be NULL if all registers are transferred with regsets or regsets
+ are not supported. */
+ unsigned char *regset_bitmap;
+
+ /* Info used when accessing registers with PTRACE_PEEKUSER /
+ PTRACE_POKEUSER. This can be NULL if all registers are
+ transferred with regsets .*/
+ struct usrregs_info *usrregs;
+
+#ifdef HAVE_LINUX_REGSETS
+ /* Info used when accessing registers with regsets. */
+ struct regsets_info *regsets_info;
#endif
+};

struct process_info_private
{
@@ -60,6 +110,11 @@ struct process_info_private

/* &_r_debug. 0 if not yet determined. -1 if no PT_DYNAMIC in Phdrs. */
CORE_ADDR r_debug;
+
+ /* This flag is true iff we've just created or attached to the first
+ LWP of this process but it has not stopped yet. As soon as it
+ does, we need to call the low target's arch_setup callback. */
+ int new_inferior;
};

struct lwp_info;
@@ -69,14 +124,7 @@ struct linux_target_ops
/* Architecture-specific setup. */
void (*arch_setup) (void);

- int num_regs;
- int *regmap;
-
- /* Regset support bitmap: 1 for registers that are transferred as a part
- of a regset, 0 for ones that need to be handled individually. This
- can be NULL if all registers are transferred with regsets or regsets
- are not supported. */
- unsigned char *regset_bitmap;
+ const struct regs_info *(*regs_info) (void);
int (*cannot_fetch_register) (int);

/* Returns 0 if we can store the register, 1 if we can not
@@ -294,6 +342,12 @@ void linux_attach_lwp (unsigned long pid);
struct lwp_info *find_lwp_pid (ptid_t ptid);
void linux_stop_lwp (struct lwp_info *lwp);

+#ifdef HAVE_LINUX_REGSETS
+void initialize_regsets_info (struct regsets_info *regsets_info);
+#endif
+
+void initialize_low_arch (void);
+
/* From thread-db.c */
int thread_db_init (int use_events);
void thread_db_detach (struct process_info *);
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 1d1df95..1457062 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -30,21 +30,43 @@

#include "gdb_proc_service.h"
#include "agent.h"
+#include "tdesc.h"

-/* Defined in auto-generated file i386-linux.c. */
-void init_registers_i386_linux (void);
+#ifdef __x86_64__
/* Defined in auto-generated file amd64-linux.c. */
void init_registers_amd64_linux (void);
-/* Defined in auto-generated file i386-avx-linux.c. */
-void init_registers_i386_avx_linux (void);
+extern const struct target_desc *tdesc_amd64_linux;
+
/* Defined in auto-generated file amd64-avx-linux.c. */
void init_registers_amd64_avx_linux (void);
-/* Defined in auto-generated file i386-mmx-linux.c. */
-void init_registers_i386_mmx_linux (void);
+extern const struct target_desc *tdesc_amd64_avx_linux;
+
/* Defined in auto-generated file x32-linux.c. */
void init_registers_x32_linux (void);
+extern const struct target_desc *tdesc_x32_linux;
+
/* Defined in auto-generated file x32-avx-linux.c. */
void init_registers_x32_avx_linux (void);
+extern const struct target_desc *tdesc_x32_avx_linux;
+#endif
+
+/* Defined in auto-generated file i386-linux.c. */
+void init_registers_i386_linux (void);
+extern const struct target_desc *tdesc_i386_linux;
+
+/* Defined in auto-generated file i386-mmx-linux.c. */
+void init_registers_i386_mmx_linux (void);
+extern const struct target_desc *tdesc_i386_mmx_linux;
+
+/* Defined in auto-generated file i386-avx-linux.c. */
+void init_registers_i386_avx_linux (void);
+extern const struct target_desc *tdesc_i386_avx_linux;
+
+#ifdef __x86_64__
+static struct target_desc *tdesc_amd64_linux_no_xml;
+#endif
+static struct target_desc *tdesc_i386_linux_no_xml;
+

static unsigned char jump_insn[] = { 0xe9, 0, 0, 0, 0 };
static unsigned char small_jump_insn[] = { 0x66, 0xe9, 0, 0 };
@@ -160,6 +182,22 @@ static /*const*/ int i386_regmap[] =
#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))

#endif
+
+#ifdef __x86_64__
+
+/* Returns true if the current inferior belongs to a x86-64 process,
+ per the tdesc. */
+
+static int
+is_64bit_tdesc (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_inferior, 0);
+
+ return register_size (regcache->tdesc, 0) == 8;
+}
+
+#endif
+
\f
/* Called by libthread_db. */

@@ -168,7 +206,7 @@ ps_get_thread_area (const struct ps_prochandle *ph,
lwpid_t lwpid, int idx, void **base)
{
#ifdef __x86_64__
- int use_64bit = register_size (0) == 8;
+ int use_64bit = is_64bit_tdesc ();

if (use_64bit)
{
@@ -210,7 +248,7 @@ static int
x86_get_thread_area (int lwpid, CORE_ADDR *addr)
{
#ifdef __x86_64__
- int use_64bit = register_size (0) == 8;
+ int use_64bit = is_64bit_tdesc ();

if (use_64bit)
{
@@ -250,14 +288,24 @@ x86_get_thread_area (int lwpid, CORE_ADDR *addr)

\f
static int
-i386_cannot_store_register (int regno)
+x86_cannot_store_register (int regno)
{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return 0;
+#endif
+
return regno >= I386_NUM_REGS;
}

static int
-i386_cannot_fetch_register (int regno)
+x86_cannot_fetch_register (int regno)
{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return 0;
+#endif
+
return regno >= I386_NUM_REGS;
}

@@ -267,7 +315,7 @@ x86_fill_gregset (struct regcache *regcache, void *buf)
int i;

#ifdef __x86_64__
- if (register_size (0) == 8)
+ if (register_size (regcache->tdesc, 0) == 8)
{
for (i = 0; i < X86_64_NUM_REGS; i++)
if (x86_64_regmap[i] != -1)
@@ -289,7 +337,7 @@ x86_store_gregset (struct regcache *regcache, const void *buf)
int i;

#ifdef __x86_64__
- if (register_size (0) == 8)
+ if (register_size (regcache->tdesc, 0) == 8)
{
for (i = 0; i < X86_64_NUM_REGS; i++)
if (x86_64_regmap[i] != -1)
@@ -358,11 +406,9 @@ x86_store_xstateregset (struct regcache *regcache, const void *buf)
This is, presumably, to handle the case where PTRACE_[GS]ETFPXREGS
doesn't work. IWBN to avoid the duplication in the case where it
does work. Maybe the arch_setup routine could check whether it works
- and update target_regsets accordingly, maybe by moving target_regsets
- to linux_target_ops and set the right one there, rather than having to
- modify the target_regsets global. */
+ and update the supported regsets accordingly. */

-struct regset_info target_regsets[] =
+static struct regset_info x86_regsets[] =
{
#ifdef HAVE_PTRACE_GETREGS
{ PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
@@ -387,7 +433,7 @@ struct regset_info target_regsets[] =
static CORE_ADDR
x86_get_pc (struct regcache *regcache)
{
- int use_64bit = register_size (0) == 8;
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;

if (use_64bit)
{
@@ -406,7 +452,7 @@ x86_get_pc (struct regcache *regcache)
static void
x86_set_pc (struct regcache *regcache, CORE_ADDR pc)
{
- int use_64bit = register_size (0) == 8;
+ int use_64bit = register_size (regcache->tdesc, 0) == 8;

if (use_64bit)
{
@@ -1106,7 +1152,7 @@ x86_siginfo_fixup (siginfo_t *native, void *inf, int direction)
{
#ifdef __x86_64__
/* Is the inferior 32-bit? If so, then fixup the siginfo object. */
- if (register_size (0) == 4)
+ if (!is_64bit_tdesc ())
{
if (sizeof (siginfo_t) != sizeof (compat_siginfo_t))
fatal ("unexpected difference in siginfo");
@@ -1140,138 +1186,207 @@ x86_siginfo_fixup (siginfo_t *native, void *inf, int direction)
\f
static int use_xml;

-/* Update gdbserver_xmltarget. */
+/* Format of XSAVE extended state is:
+ struct
+ {
+ fxsave_bytes[0..463]
+ sw_usable_bytes[464..511]
+ xstate_hdr_bytes[512..575]
+ avx_bytes[576..831]
+ future_state etc
+ };
+
+ Same memory layout will be used for the coredump NT_X86_XSTATE
+ representing the XSAVE extended state registers.
+
+ The first 8 bytes of the sw_usable_bytes[464..467] is the OS enabled
+ extended state mask, which is the same as the extended control register
+ 0 (the XFEATURE_ENABLED_MASK register), XCR0. We can use this mask
+ together with the mask saved in the xstate_hdr_bytes to determine what
+ states the processor/OS supports and what state, used or initialized,
+ the process/thread is in. */
+#define I386_LINUX_XSAVE_XCR0_OFFSET 464
+
+/* Does the current host support the GETFPXREGS request? The header
+ file may or may not define it, and even if it is defined, the
+ kernel will return EIO if it's running on a pre-SSE processor. */
+int have_ptrace_getfpxregs =
+#ifdef HAVE_PTRACE_GETFPXREGS
+ -1
+#else
+ 0
+#endif
+;

-static void
-x86_linux_update_xmltarget (void)
+/* Does the current host support PTRACE_GETREGSET? */
+static int have_ptrace_getregset = -1;
+
+/* Get Linux/x86 target description from running target. */
+
+static const struct target_desc *
+x86_linux_read_description (void)
{
- int pid;
+ unsigned int machine;
+ int is_elf64;
+ int avx;
+ int tid;
+ static uint64_t xcr0;
struct regset_info *regset;
- static unsigned long long xcr0;
- static int have_ptrace_getregset = -1;
-#if !defined(__x86_64__) && defined(HAVE_PTRACE_GETFPXREGS)
- static int have_ptrace_getfpxregs = -1;
-#endif

- if (!current_inferior)
- return;
+ tid = lwpid_of (get_thread_lwp (current_inferior));

- /* Before changing the register cache internal layout or the target
- regsets, flush the contents of the current valid caches back to
- the threads. */
- regcache_invalidate ();
+ is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);

- pid = pid_of (get_thread_lwp (current_inferior));
-#ifdef __x86_64__
- if (num_xmm_registers == 8)
- init_registers_i386_linux ();
- else if (linux_is_elf64)
- init_registers_amd64_linux ();
- else
- init_registers_x32_linux ();
-#else
+ if (sizeof (void *) == 4)
{
-# ifdef HAVE_PTRACE_GETFPXREGS
- if (have_ptrace_getfpxregs == -1)
- {
- elf_fpxregset_t fpxregs;
+ if (is_elf64 > 0)
+ error (_("Can't debug 64-bit process with 32-bit GDBserver"));
+#ifndef __x86_64__
+ else if (machine == EM_X86_64)
+ error (_("Can't debug x86-64 process with 32-bit GDBserver"));
+#endif
+ }

- if (ptrace (PTRACE_GETFPXREGS, pid, 0, (int) &fpxregs) < 0)
- {
- have_ptrace_getfpxregs = 0;
- x86_xcr0 = I386_XSTATE_X87_MASK;
-
- /* Disable PTRACE_GETFPXREGS. */
- for (regset = target_regsets;
- regset->fill_function != NULL; regset++)
- if (regset->get_request == PTRACE_GETFPXREGS)
- {
- regset->size = 0;
- break;
- }
- }
- else
- have_ptrace_getfpxregs = 1;
- }
+#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
+ if (machine == EM_386 && have_ptrace_getfpxregs == -1)
+ {
+ elf_fpxregset_t fpxregs;

- if (!have_ptrace_getfpxregs)
+ if (ptrace (PTRACE_GETFPXREGS, tid, 0, (long) &fpxregs) < 0)
{
- init_registers_i386_mmx_linux ();
- return;
+ have_ptrace_getfpxregs = 0;
+ have_ptrace_getregset = 0;
+ return tdesc_i386_mmx_linux;
}
-# endif
- init_registers_i386_linux ();
+ else
+ have_ptrace_getfpxregs = 1;
}
#endif

if (!use_xml)
{
+ x86_xcr0 = I386_XSTATE_SSE_MASK;
+
/* Don't use XML. */
#ifdef __x86_64__
- if (num_xmm_registers == 8)
- gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
+ if (machine == EM_X86_64)
+ return tdesc_amd64_linux_no_xml;
else
- gdbserver_xmltarget = xmltarget_amd64_linux_no_xml;
-#else
- gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
#endif
-
- x86_xcr0 = I386_XSTATE_SSE_MASK;
-
- return;
+ return tdesc_i386_linux_no_xml;
}

- /* Check if XSAVE extended state is supported. */
if (have_ptrace_getregset == -1)
{
- unsigned long long xstateregs[I386_XSTATE_SSE_SIZE / sizeof (long long)];
+ uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))];
struct iovec iov;

iov.iov_base = xstateregs;
iov.iov_len = sizeof (xstateregs);

/* Check if PTRACE_GETREGSET works. */
- if (ptrace (PTRACE_GETREGSET, pid, (unsigned int) NT_X86_XSTATE,
- &iov) < 0)
+ if (ptrace (PTRACE_GETREGSET, tid,
+ (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+ have_ptrace_getregset = 0;
+ else
{
- have_ptrace_getregset = 0;
- return;
+ have_ptrace_getregset = 1;
+
+ /* Get XCR0 from XSAVE extended state. */
+ xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+ / sizeof (uint64_t))];
+
+ /* Use PTRACE_GETREGSET if it is available. */
+ for (regset = x86_regsets;
+ regset->fill_function != NULL; regset++)
+ if (regset->get_request == PTRACE_GETREGSET)
+ regset->size = I386_XSTATE_SIZE (xcr0);
+ else if (regset->type != GENERAL_REGS)
+ regset->size = 0;
}
- else
- have_ptrace_getregset = 1;
-
- /* Get XCR0 from XSAVE extended state at byte 464. */
- xcr0 = xstateregs[464 / sizeof (long long)];
-
- /* Use PTRACE_GETREGSET if it is available. */
- for (regset = target_regsets;
- regset->fill_function != NULL; regset++)
- if (regset->get_request == PTRACE_GETREGSET)
- regset->size = I386_XSTATE_SIZE (xcr0);
- else if (regset->type != GENERAL_REGS)
- regset->size = 0;
}

- if (have_ptrace_getregset)
- {
- /* AVX is the highest feature we support. */
- if ((xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
- {
- x86_xcr0 = xcr0;
+ /* Check the native XCR0 only if PTRACE_GETREGSET is available. */
+ avx = (have_ptrace_getregset
+ && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK);
+
+ /* AVX is the highest feature we support. */
+ if (avx)
+ x86_xcr0 = xcr0;

+ if (machine == EM_X86_64)
+ {
#ifdef __x86_64__
- /* I386 has 8 xmm regs. */
- if (num_xmm_registers == 8)
- init_registers_i386_avx_linux ();
- else if (linux_is_elf64)
- init_registers_amd64_avx_linux ();
+ if (avx)
+ {
+ if (!is_elf64)
+ return tdesc_x32_avx_linux;
else
- init_registers_x32_avx_linux ();
-#else
- init_registers_i386_avx_linux ();
-#endif
+ return tdesc_amd64_avx_linux;
+ }
+ else
+ {
+ if (!is_elf64)
+ return tdesc_x32_linux;
+ else
+ return tdesc_amd64_linux;
}
+#endif
}
+ else
+ {
+ if (avx)
+ return tdesc_i386_avx_linux;
+ else
+ return tdesc_i386_linux;
+ }
+
+ gdb_assert_not_reached ("failed to return tdesc");
+}
+
+/* Callback for find_inferior. Stops iteration when a thread with a
+ given PID is found. */
+
+static int
+same_process_callback (struct inferior_list_entry *entry, void *data)
+{
+ int pid = *(int *) data;
+
+ return (ptid_get_pid (entry->id) == pid);
+}
+
+/* Callback for for_each_inferior. Calls the arch_setup routine for
+ each process. */
+
+static void
+x86_arch_setup_process_callback (struct inferior_list_entry *entry)
+{
+ int pid = ptid_get_pid (entry->id);
+
+ /* Look up any thread of this processes. */
+ current_inferior
+ = (struct thread_info *) find_inferior (&all_threads,
+ same_process_callback, &pid);
+
+ the_low_target.arch_setup ();
+}
+
+/* Update all the target description of all processes; a new GDB
+ connected, and it may or not support xml target descriptions. */
+
+static void
+x86_linux_update_xmltarget (void)
+{
+ struct thread_info *save_inferior = current_inferior;
+
+ /* Before changing the register cache's internal layout, flush the
+ contents of the current valid caches back to the threads, and
+ release the current regcache objects. */
+ regcache_release ();
+
+ for_each_inferior (&all_processes, x86_arch_setup_process_callback);
+
+ current_inferior = save_inferior;
}

/* Process qSupported query, "xmlRegisters=". Update the buffer size for
@@ -1304,62 +1419,54 @@ x86_linux_process_qsupported (const char *query)
x86_linux_update_xmltarget ();
}

-/* Initialize gdbserver for the architecture of the inferior. */
+/* Common for x86/x86-64. */

-static void
-x86_arch_setup (void)
-{
- int pid = pid_of (get_thread_lwp (current_inferior));
- unsigned int machine;
- int is_elf64 = linux_pid_exe_is_elf_64_file (pid, &machine);
-
- if (sizeof (void *) == 4)
- {
- if (is_elf64 > 0)
- error (_("Can't debug 64-bit process with 32-bit GDBserver"));
-#ifndef __x86_64__
- else if (machine == EM_X86_64)
- error (_("Can't debug x86-64 process with 32-bit GDBserver"));
-#endif
- }
+static struct regsets_info x86_regsets_info =
+ {
+ x86_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };

#ifdef __x86_64__
- if (is_elf64 < 0)
- {
- /* This can only happen if /proc/<pid>/exe is unreadable,
- but "that can't happen" if we've gotten this far.
- Fall through and assume this is a 32-bit program. */
- }
- else if (machine == EM_X86_64)
- {
- /* Amd64 doesn't have HAVE_LINUX_USRREGS. */
- the_low_target.num_regs = -1;
- the_low_target.regmap = NULL;
- the_low_target.cannot_fetch_register = NULL;
- the_low_target.cannot_store_register = NULL;
-
- /* Amd64 has 16 xmm regs. */
- num_xmm_registers = 16;
-
- linux_is_elf64 = is_elf64;
- x86_linux_update_xmltarget ();
- return;
- }
-
- linux_is_elf64 = 0;
+static struct regs_info amd64_linux_regs_info =
+ {
+ NULL, /* regset_bitmap */
+ NULL, /* usrregs_info */
+ &x86_regsets_info
+ };
#endif
+static struct usrregs_info i386_linux_usrregs_info =
+ {
+ I386_NUM_REGS,
+ i386_regmap,
+ };

- /* Ok we have a 32-bit inferior. */
+static struct regs_info i386_linux_regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &i386_linux_usrregs_info,
+ &x86_regsets_info
+ };

- the_low_target.num_regs = I386_NUM_REGS;
- the_low_target.regmap = i386_regmap;
- the_low_target.cannot_fetch_register = i386_cannot_fetch_register;
- the_low_target.cannot_store_register = i386_cannot_store_register;
+const struct regs_info *
+x86_linux_regs_info (void)
+{
+#ifdef __x86_64__
+ if (is_64bit_tdesc ())
+ return &amd64_linux_regs_info;
+ else
+#endif
+ return &i386_linux_regs_info;
+}

- /* I386 has 8 xmm regs. */
- num_xmm_registers = 8;
+/* Initialize the target description for the architecture of the
+ inferior. */

- x86_linux_update_xmltarget ();
+static void
+x86_arch_setup (void)
+{
+ current_process ()->tdesc = x86_linux_read_description ();
}

static int
@@ -1789,7 +1896,7 @@ x86_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
char *err)
{
#ifdef __x86_64__
- if (register_size (0) == 8)
+ if (is_64bit_tdesc ())
return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
collector, lockaddr,
orig_size, jump_entry,
@@ -1823,7 +1930,7 @@ x86_get_min_fast_tracepoint_insn_len (void)
#ifdef __x86_64__
/* On x86-64, 5-byte jump instructions with a 4-byte offset are always
used for fast tracepoints. */
- if (register_size (0) == 8)
+ if (is_64bit_tdesc ())
return 5;
#endif

@@ -3166,9 +3273,7 @@ static struct emit_ops *
x86_emit_ops (void)
{
#ifdef __x86_64__
- int use_64bit = register_size (0) == 8;
-
- if (use_64bit)
+ if (is_64bit_tdesc ())
return &amd64_emit_ops;
else
#endif
@@ -3187,11 +3292,9 @@ x86_supports_range_stepping (void)
struct linux_target_ops the_low_target =
{
x86_arch_setup,
- -1,
- NULL,
- NULL,
- NULL,
- NULL,
+ x86_linux_regs_info,
+ x86_cannot_fetch_register,
+ x86_cannot_store_register,
NULL, /* fetch_register */
x86_get_pc,
x86_set_pc,
@@ -3222,3 +3325,27 @@ struct linux_target_ops the_low_target =
x86_get_min_fast_tracepoint_insn_len,
x86_supports_range_stepping,
};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+#ifdef __x86_64__
+ init_registers_amd64_linux ();
+ init_registers_amd64_avx_linux ();
+ init_registers_x32_linux ();
+
+ tdesc_amd64_linux_no_xml = xmalloc (sizeof (struct target_desc));
+ copy_target_description (tdesc_amd64_linux_no_xml, tdesc_amd64_linux);
+ tdesc_amd64_linux_no_xml->xmltarget = xmltarget_amd64_linux_no_xml;
+#endif
+ init_registers_i386_linux ();
+ init_registers_i386_mmx_linux ();
+ init_registers_i386_avx_linux ();
+
+ tdesc_i386_linux_no_xml = xmalloc (sizeof (struct target_desc));
+ copy_target_description (tdesc_i386_linux_no_xml, tdesc_i386_linux);
+ tdesc_i386_linux_no_xml->xmltarget = xmltarget_i386_linux_no_xml;
+
+ initialize_regsets_info (&x86_regsets_info);
+}
diff --git a/gdb/gdbserver/proc-service.c b/gdb/gdbserver/proc-service.c
index 235bab7..69b86f8 100644
--- a/gdb/gdbserver/proc-service.c
+++ b/gdb/gdbserver/proc-service.c
@@ -39,18 +39,20 @@ typedef size_t gdb_ps_size_t;

#ifdef HAVE_REGSETS
static struct regset_info *
-gregset_info(void)
+gregset_info (void)
{
int i = 0;
+ const struct regs_info *regs_info = (*the_low_target.regs_info) ();
+ struct regsets_info *regsets_info = regs_info->regsets_info;

- while (target_regsets[i].size != -1)
+ while (regsets_info->regsets[i].size != -1)
{
- if (target_regsets[i].type == GENERAL_REGS)
+ if (regsets_info->regsets[i].type == GENERAL_REGS)
break;
i++;
}

- return &target_regsets[i];
+ return &regsets_info->regsets[i];
}
#endif

diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c
index 778bd05..fbc4e31 100644
--- a/gdb/gdbserver/regcache.c
+++ b/gdb/gdbserver/regcache.c
@@ -19,17 +19,11 @@
#include "server.h"
#include "regdef.h"
#include "gdbthread.h"
+#include "tdesc.h"

#include <stdlib.h>
#include <string.h>

-static int register_bytes;
-
-static struct reg *reg_defs;
-static int num_registers;
-
-const char **gdbserver_expedite_regs;
-
#ifndef IN_PROCESS_AGENT

struct regcache *
@@ -40,7 +34,15 @@ get_thread_regcache (struct thread_info *thread, int fetch)
regcache = (struct regcache *) inferior_regcache_data (thread);

if (regcache == NULL)
- fatal ("no register cache");
+ {
+ struct process_info *proc = get_thread_process (thread);
+
+ if (proc->tdesc == NULL)
+ fatal ("no target description");
+
+ regcache = new_register_cache (proc->tdesc);
+ set_inferior_regcache_data (thread, regcache);
+ }

if (fetch && regcache->registers_valid == 0)
{
@@ -56,9 +58,8 @@ get_thread_regcache (struct thread_info *thread, int fetch)
}

void
-regcache_invalidate_one (struct inferior_list_entry *entry)
+regcache_invalidate_thread (struct thread_info *thread)
{
- struct thread_info *thread = (struct thread_info *) entry;
struct regcache *regcache;

regcache = (struct regcache *) inferior_regcache_data (thread);
@@ -78,16 +79,35 @@ regcache_invalidate_one (struct inferior_list_entry *entry)
regcache->registers_valid = 0;
}

+static int
+regcache_invalidate_one (struct inferior_list_entry *entry,
+ void *pid_p)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ int pid = *(int *) pid_p;
+
+ /* Only invalidate the regcaches of threads of this process. */
+ if (ptid_get_pid (entry->id) == pid)
+ regcache_invalidate_thread (thread);
+
+ return 0;
+}
+
void
regcache_invalidate (void)
{
- for_each_inferior (&all_threads, regcache_invalidate_one);
+ /* Only update the threads of the current process. */
+ int pid = ptid_get_pid (current_inferior->entry.id);
+
+ find_inferior (&all_threads, regcache_invalidate_one, &pid);
}

#endif

struct regcache *
-init_register_cache (struct regcache *regcache, unsigned char *regbuf)
+init_register_cache (struct regcache *regcache,
+ const struct target_desc *tdesc,
+ unsigned char *regbuf)
{
#ifndef IN_PROCESS_AGENT
if (regbuf == NULL)
@@ -96,9 +116,10 @@ init_register_cache (struct regcache *regcache, unsigned char *regbuf)
created, in case there are registers the target never
fetches. This way they'll read as zero instead of
garbage. */
- regcache->registers = xcalloc (1, register_bytes);
+ regcache->tdesc = tdesc;
+ regcache->registers = xcalloc (1, tdesc->registers_size);
regcache->registers_owned = 1;
- regcache->register_status = xcalloc (1, num_registers);
+ regcache->register_status = xcalloc (1, tdesc->num_registers);
gdb_assert (REG_UNAVAILABLE == 0);
}
else
@@ -108,6 +129,7 @@ init_register_cache (struct regcache *regcache, unsigned char *regbuf)
else
#endif
{
+ regcache->tdesc = tdesc;
regcache->registers = regbuf;
regcache->registers_owned = 0;
#ifndef IN_PROCESS_AGENT
@@ -123,15 +145,14 @@ init_register_cache (struct regcache *regcache, unsigned char *regbuf)
#ifndef IN_PROCESS_AGENT

struct regcache *
-new_register_cache (void)
+new_register_cache (const struct target_desc *tdesc)
{
struct regcache *regcache;

- if (register_bytes == 0)
- return NULL; /* The architecture hasn't been initialized yet. */
+ gdb_assert (tdesc->registers_size != 0);

regcache = xmalloc (sizeof (*regcache));
- return init_register_cache (regcache, NULL);
+ return init_register_cache (regcache, tdesc, NULL);
}

void
@@ -151,67 +172,19 @@ free_register_cache (struct regcache *regcache)
void
regcache_cpy (struct regcache *dst, struct regcache *src)
{
- memcpy (dst->registers, src->registers, register_bytes);
+ gdb_assert (src != NULL && dst != NULL);
+ gdb_assert (src->tdesc == dst->tdesc);
+ gdb_assert (src != dst);
+
+ memcpy (dst->registers, src->registers, src->tdesc->registers_size);
#ifndef IN_PROCESS_AGENT
if (dst->register_status != NULL && src->register_status != NULL)
- memcpy (dst->register_status, src->register_status, num_registers);
+ memcpy (dst->register_status, src->register_status,
+ src->tdesc->num_registers);
#endif
dst->registers_valid = src->registers_valid;
}

-#ifndef IN_PROCESS_AGENT
-static void
-realloc_register_cache (struct inferior_list_entry *thread_p)
-{
- struct thread_info *thread = (struct thread_info *) thread_p;
- struct regcache *regcache
- = (struct regcache *) inferior_regcache_data (thread);
-
- if (regcache != NULL)
- regcache_invalidate_one (thread_p);
- free_register_cache (regcache);
- set_inferior_regcache_data (thread, new_register_cache ());
-}
-#endif
-
-void
-set_register_cache (struct reg *regs, int n)
-{
- int offset, i;
-
-#ifndef IN_PROCESS_AGENT
- /* Before changing the register cache internal layout, flush the
- contents of valid caches back to the threads. */
- regcache_invalidate ();
-#endif
-
- reg_defs = regs;
- num_registers = n;
-
- offset = 0;
- for (i = 0; i < n; i++)
- {
- regs[i].offset = offset;
- offset += regs[i].size;
- }
-
- register_bytes = offset / 8;
-
- /* Make sure PBUFSIZ is large enough to hold a full register packet. */
- if (2 * register_bytes + 32 > PBUFSIZ)
- fatal ("Register packet size exceeds PBUFSIZ.");
-
-#ifndef IN_PROCESS_AGENT
- /* Re-allocate all pre-existing register caches. */
- for_each_inferior (&all_threads, realloc_register_cache);
-#endif
-}
-
-int
-register_cache_size (void)
-{
- return register_bytes;
-}

#ifndef IN_PROCESS_AGENT

@@ -219,21 +192,23 @@ void
registers_to_string (struct regcache *regcache, char *buf)
{
unsigned char *registers = regcache->registers;
+ const struct target_desc *tdesc = regcache->tdesc;
int i;

- for (i = 0; i < num_registers; i++)
+ for (i = 0; i < tdesc->num_registers; i++)
{
if (regcache->register_status[i] == REG_VALID)
{
- convert_int_to_ascii (registers, buf, register_size (i));
- buf += register_size (i) * 2;
+ convert_int_to_ascii (registers, buf,
+ register_size (tdesc, i));
+ buf += register_size (tdesc, i) * 2;
}
else
{
- memset (buf, 'x', register_size (i) * 2);
- buf += register_size (i) * 2;
+ memset (buf, 'x', register_size (tdesc, i) * 2);
+ buf += register_size (tdesc, i) * 2;
}
- registers += register_size (i);
+ registers += register_size (tdesc, i);
}
*buf = '\0';
}
@@ -243,59 +218,97 @@ registers_from_string (struct regcache *regcache, char *buf)
{
int len = strlen (buf);
unsigned char *registers = regcache->registers;
+ const struct target_desc *tdesc = regcache->tdesc;

- if (len != register_bytes * 2)
+ if (len != tdesc->registers_size * 2)
{
warning ("Wrong sized register packet (expected %d bytes, got %d)",
- 2*register_bytes, len);
- if (len > register_bytes * 2)
- len = register_bytes * 2;
+ 2 * tdesc->registers_size, len);
+ if (len > tdesc->registers_size * 2)
+ len = tdesc->registers_size * 2;
}
convert_ascii_to_int (buf, registers, len / 2);
}

struct reg *
-find_register_by_name (const char *name)
+find_register_by_name (const struct target_desc *tdesc, const char *name)
{
int i;

- for (i = 0; i < num_registers; i++)
- if (!strcmp (name, reg_defs[i].name))
- return &reg_defs[i];
+ for (i = 0; i < tdesc->num_registers; i++)
+ if (strcmp (name, tdesc->reg_defs[i].name) == 0)
+ return &tdesc->reg_defs[i];
fatal ("Unknown register %s requested", name);
return 0;
}

int
-find_regno (const char *name)
+find_regno (const struct target_desc *tdesc, const char *name)
{
int i;

- for (i = 0; i < num_registers; i++)
- if (!strcmp (name, reg_defs[i].name))
+ for (i = 0; i < tdesc->num_registers; i++)
+ if (strcmp (name, tdesc->reg_defs[i].name) == 0)
return i;
fatal ("Unknown register %s requested", name);
return -1;
}

struct reg *
-find_register_by_number (int n)
+find_register_by_number (const struct target_desc *tdesc, int n)
{
- return &reg_defs[n];
+ return &tdesc->reg_defs[n];
}

#endif

+#ifndef IN_PROCESS_AGENT
+static void
+free_register_cache_thread (struct thread_info *thread)
+{
+ struct regcache *regcache
+ = (struct regcache *) inferior_regcache_data (thread);
+
+ if (regcache != NULL)
+ {
+ regcache_invalidate_thread (thread);
+ free_register_cache (regcache);
+ set_inferior_regcache_data (thread, NULL);
+ }
+}
+
+static void
+free_register_cache_thread_one (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ free_register_cache_thread (thread);
+}
+
+void
+regcache_release (void)
+{
+ /* Flush and release all pre-existing register caches. */
+ for_each_inferior (&all_threads, free_register_cache_thread_one);
+}
+#endif
+
int
-register_size (int n)
+register_cache_size (const struct target_desc *tdesc)
{
- return reg_defs[n].size / 8;
+ return tdesc->registers_size;
+}
+
+int
+register_size (const struct target_desc *tdesc, int n)
+{
+ return tdesc->reg_defs[n].size / 8;
}

static unsigned char *
register_data (struct regcache *regcache, int n, int fetch)
{
- return regcache->registers + (reg_defs[n].offset / 8);
+ return regcache->registers + regcache->tdesc->reg_defs[n].offset / 8;
}

/* Supply register N, whose contents are stored in BUF, to REGCACHE.
@@ -307,7 +320,8 @@ supply_register (struct regcache *regcache, int n, const void *buf)
{
if (buf)
{
- memcpy (register_data (regcache, n, 0), buf, register_size (n));
+ memcpy (register_data (regcache, n, 0), buf,
+ register_size (regcache->tdesc, n));
#ifndef IN_PROCESS_AGENT
if (regcache->register_status != NULL)
regcache->register_status[n] = REG_VALID;
@@ -315,7 +329,8 @@ supply_register (struct regcache *regcache, int n, const void *buf)
}
else
{
- memset (register_data (regcache, n, 0), 0, register_size (n));
+ memset (register_data (regcache, n, 0), 0,
+ register_size (regcache->tdesc, n));
#ifndef IN_PROCESS_AGENT
if (regcache->register_status != NULL)
regcache->register_status[n] = REG_UNAVAILABLE;
@@ -328,7 +343,8 @@ supply_register (struct regcache *regcache, int n, const void *buf)
void
supply_register_zeroed (struct regcache *regcache, int n)
{
- memset (register_data (regcache, n, 0), 0, register_size (n));
+ memset (register_data (regcache, n, 0), 0,
+ register_size (regcache->tdesc, n));
#ifndef IN_PROCESS_AGENT
if (regcache->register_status != NULL)
regcache->register_status[n] = REG_VALID;
@@ -344,24 +360,28 @@ supply_regblock (struct regcache *regcache, const void *buf)
{
if (buf)
{
- memcpy (regcache->registers, buf, register_bytes);
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ memcpy (regcache->registers, buf, tdesc->registers_size);
#ifndef IN_PROCESS_AGENT
{
int i;

- for (i = 0; i < num_registers; i++)
+ for (i = 0; i < tdesc->num_registers; i++)
regcache->register_status[i] = REG_VALID;
}
#endif
}
else
{
- memset (regcache->registers, 0, register_bytes);
+ const struct target_desc *tdesc = regcache->tdesc;
+
+ memset (regcache->registers, 0, tdesc->registers_size);
#ifndef IN_PROCESS_AGENT
{
int i;

- for (i = 0; i < num_registers; i++)
+ for (i = 0; i < tdesc->num_registers; i++)
regcache->register_status[i] = REG_UNAVAILABLE;
}
#endif
@@ -374,7 +394,7 @@ void
supply_register_by_name (struct regcache *regcache,
const char *name, const void *buf)
{
- supply_register (regcache, find_regno (name), buf);
+ supply_register (regcache, find_regno (regcache->tdesc, name), buf);
}

#endif
@@ -382,7 +402,8 @@ supply_register_by_name (struct regcache *regcache,
void
collect_register (struct regcache *regcache, int n, void *buf)
{
- memcpy (buf, register_data (regcache, n, 1), register_size (n));
+ memcpy (buf, register_data (regcache, n, 1),
+ register_size (regcache->tdesc, n));
}

#ifndef IN_PROCESS_AGENT
@@ -390,15 +411,15 @@ collect_register (struct regcache *regcache, int n, void *buf)
void
collect_register_as_string (struct regcache *regcache, int n, char *buf)
{
- convert_int_to_ascii (register_data (regcache, n, 1),
- buf, register_size (n));
+ convert_int_to_ascii (register_data (regcache, n, 1), buf,
+ register_size (regcache->tdesc, n));
}

void
collect_register_by_name (struct regcache *regcache,
const char *name, void *buf)
{
- collect_register (regcache, find_regno (name), buf);
+ collect_register (regcache, find_regno (regcache->tdesc, name), buf);
}

/* Special handling for register PC. */
diff --git a/gdb/gdbserver/regcache.h b/gdb/gdbserver/regcache.h
index ce86322..700a2fe 100644
--- a/gdb/gdbserver/regcache.h
+++ b/gdb/gdbserver/regcache.h
@@ -21,6 +21,7 @@

struct inferior_list_entry;
struct thread_info;
+struct target_desc;

/* The register exists, it has a value, but we don't know what it is.
Used when inspecting traceframes. */
@@ -35,6 +36,9 @@ struct thread_info;

struct regcache
{
+ /* The regcache's description. */
+ const struct target_desc *tdesc;
+
/* Whether the REGISTERS buffer's contents are valid. If false, we
haven't fetched the registers from the target yet. Not that this
register cache is _not_ pass-through, unlike GDB's. Note that
@@ -50,13 +54,14 @@ struct regcache
};

struct regcache *init_register_cache (struct regcache *regcache,
+ const struct target_desc *tdesc,
unsigned char *regbuf);

void regcache_cpy (struct regcache *dst, struct regcache *src);

/* Create a new register cache for INFERIOR. */

-struct regcache *new_register_cache (void);
+struct regcache *new_register_cache (const struct target_desc *tdesc);

struct regcache *get_thread_regcache (struct thread_info *thread, int fetch);

@@ -64,11 +69,20 @@ struct regcache *get_thread_regcache (struct thread_info *thread, int fetch);

void free_register_cache (struct regcache *regcache);

-/* Invalidate cached registers for one or all threads. */
+/* Invalidate cached registers for one thread. */
+
+void regcache_invalidate_thread (struct thread_info *);
+
+/* Invalidate cached registers for all threads of the current
+ process. */

-void regcache_invalidate_one (struct inferior_list_entry *);
void regcache_invalidate (void);

+/* Invalidate and release the register cache of all threads of the
+ current process. */
+
+void regcache_release (void);
+
/* Convert all registers to a string in the currently specified remote
format. */

@@ -84,18 +98,13 @@ void regcache_write_pc (struct regcache *regcache, CORE_ADDR pc);

/* Return a pointer to the description of register ``n''. */

-struct reg *find_register_by_number (int n);
-
-int register_size (int n);
+struct reg *find_register_by_number (const struct target_desc *tdesc, int n);

-int register_cache_size (void);
+int register_cache_size (const struct target_desc *tdesc);

-int find_regno (const char *name);
+int register_size (const struct target_desc *tdesc, int n);

-/* The following two variables are set by auto-generated
- code in the init_registers_... routines. */
-extern const char **gdbserver_expedite_regs;
-extern const char *gdbserver_xmltarget;
+int find_regno (const struct target_desc *tdesc, const char *name);

void supply_register (struct regcache *regcache, int n, const void *buf);

diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 42c6a54..3f055cf 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -20,6 +20,7 @@
#include "terminal.h"
#include "target.h"
#include "gdbthread.h"
+#include "tdesc.h"
#include <stdio.h>
#include <string.h>
#if HAVE_SYS_IOCTL_H
@@ -1270,7 +1271,7 @@ outreg (struct regcache *regcache, int regno, char *buf)
*buf++ = tohex (regno & 0xf);
*buf++ = ':';
collect_register_as_string (regcache, regno, buf);
- buf += 2 * register_size (regno);
+ buf += 2 * register_size (regcache->tdesc, regno);
*buf++ = ';';

return buf;
@@ -1328,12 +1329,12 @@ prepare_resume_reply (char *buf, ptid_t ptid,
sprintf (buf, "T%02x", status->value.sig);
buf += strlen (buf);

- regp = gdbserver_expedite_regs;
-
saved_inferior = current_inferior;

current_inferior = find_thread_ptid (ptid);

+ regp = current_target_desc ()->expedite_regs;
+
regcache = get_thread_regcache (current_inferior, 1);

if (the_target->stopped_by_watchpoint != NULL
@@ -1358,7 +1359,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,

while (*regp)
{
- buf = outreg (regcache, find_regno (*regp), buf);
+ buf = outreg (regcache, find_regno (regcache->tdesc, *regp), buf);
regp ++;
}
*buf = '\0';
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index d9daf84..3b16d71 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -20,6 +20,7 @@
#include "gdbthread.h"
#include "agent.h"
#include "notif.h"
+#include "tdesc.h"

#if HAVE_UNISTD_H
#include <unistd.h>
@@ -76,8 +77,6 @@ int program_signals_p;

jmp_buf toplevel;

-const char *gdbserver_xmltarget;
-
/* The PID of the originally created or attached inferior. Used to
send signals to the process when GDB sends us an asynchronous interrupt
(user hitting Control-C in the client), and to wait for the child to exit
@@ -646,21 +645,22 @@ handle_general_set (char *own_buf)
static const char *
get_features_xml (const char *annex)
{
- /* gdbserver_xmltarget defines what to return when looking
- for the "target.xml" file. Its contents can either be
- verbatim XML code (prefixed with a '@') or else the name
- of the actual XML file to be used in place of "target.xml".
+ const struct target_desc *desc = current_target_desc ();
+
+ /* `desc->xmltarget' defines what to return when looking for the
+ "target.xml" file. Its contents can either be verbatim XML code
+ (prefixed with a '@') or else the name of the actual XML file to
+ be used in place of "target.xml".

This variable is set up from the auto-generated
init_registers_... routine for the current target. */

- if (gdbserver_xmltarget
- && strcmp (annex, "target.xml") == 0)
+ if (desc->xmltarget != NULL && strcmp (annex, "target.xml") == 0)
{
- if (*gdbserver_xmltarget == '@')
- return gdbserver_xmltarget + 1;
+ if (*desc->xmltarget == '@')
+ return desc->xmltarget + 1;
else
- annex = gdbserver_xmltarget;
+ annex = desc->xmltarget;
}

#ifdef USE_XML
@@ -3294,7 +3294,8 @@ process_serial_event (void)
require_running (own_buf);
if (current_traceframe >= 0)
{
- struct regcache *regcache = new_register_cache ();
+ struct regcache *regcache
+ = new_register_cache (current_target_desc ());

if (fetch_traceframe_registers (current_traceframe,
regcache, -1) == 0)
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 139cd49..18d060c 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -112,6 +112,7 @@ struct inferior_list_entry
struct thread_info;
struct process_info;
struct regcache;
+struct target_desc;

#include "regcache.h"
#include "gdb/signals.h"
@@ -157,6 +158,8 @@ struct process_info
/* The list of installed fast tracepoints. */
struct fast_tracepoint_jump *fast_tracepoint_jumps;

+ const struct target_desc *tdesc;
+
/* Private target data. */
struct process_info_private *private;
};
@@ -431,6 +434,9 @@ void supply_static_tracepoint_registers (struct regcache *regcache,
CORE_ADDR pc);
void set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end,
char *errmsg);
+
+extern const struct target_desc *ipa_tdesc;
+
#else
void stop_tracing (void);

diff --git a/gdb/gdbserver/tdesc.c b/gdb/gdbserver/tdesc.c
new file mode 100644
index 0000000..ac3c14a
--- /dev/null
+++ b/gdb/gdbserver/tdesc.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2012-2013 Free Software Foundation, Inc.
+
+ 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 "server.h"
+#include "tdesc.h"
+#include "regdef.h"
+
+void
+init_target_desc (struct target_desc *tdesc)
+{
+ int offset, i;
+
+ offset = 0;
+ for (i = 0; i < tdesc->num_registers; i++)
+ {
+ tdesc->reg_defs[i].offset = offset;
+ offset += tdesc->reg_defs[i].size;
+ }
+
+ tdesc->registers_size = offset / 8;
+
+ /* Make sure PBUFSIZ is large enough to hold a full register
+ packet. */
+ if (2 * tdesc->registers_size + 32 > PBUFSIZ)
+ fatal ("Register packet size exceeds PBUFSIZ.");
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static const struct target_desc default_description;
+
+void
+copy_target_description (struct target_desc *dest,
+ const struct target_desc *src)
+{
+ dest->reg_defs = src->reg_defs;
+ dest->num_registers = src->num_registers;
+ dest->expedite_regs = src->expedite_regs;
+ dest->registers_size = src->registers_size;
+ dest->xmltarget = src->xmltarget;
+}
+
+const struct target_desc *
+current_target_desc (void)
+{
+ if (current_inferior == NULL)
+ return &default_description;
+
+ return current_process ()->tdesc;
+}
+
+#endif
diff --git a/gdb/gdbserver/tdesc.h b/gdb/gdbserver/tdesc.h
new file mode 100644
index 0000000..f3d018d
--- /dev/null
+++ b/gdb/gdbserver/tdesc.h
@@ -0,0 +1,64 @@
+/* Target description definitions for remote server for GDB.
+ Copyright (C) 2012-2013 Free Software Foundation, Inc.
+
+ 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/>. */
+
+#ifndef TDESC_H
+#define TDESC_H
+
+struct reg;
+
+/* A target description. */
+
+struct target_desc
+{
+ /* An array of NUM_REGISTERS elements of register definitions that
+ describe the inferior's register set. */
+ struct reg *reg_defs;
+
+ /* The number of registers in inferior's register set (and thus in
+ the regcache). */
+ int num_registers;
+
+ /* The register cache size, in bytes. */
+ int registers_size;
+
+ /* An array of register names. These are the "expedite" registers:
+ registers whose values are sent along with stop replies. */
+ const char **expedite_regs;
+
+ /* Defines what to return when looking for the "target.xml" file in
+ response to qXfer:features:read. Its contents can either be
+ verbatim XML code (prefixed with a '@') or else the name of the
+ actual XML file to be used in place of "target.xml". */
+ const char *xmltarget;
+};
+
+/* Copy target description SRC to DEST. */
+
+void copy_target_description (struct target_desc *dest,
+ const struct target_desc *src);
+
+/* Initialize TDESC. */
+
+void init_target_desc (struct target_desc *tdesc);
+
+/* Return the current inferior's target description. Never returns
+ NULL. */
+
+const struct target_desc *current_target_desc (void);
+
+#endif /* TDESC_H */
diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c
index 1ff6114..d237e7f 100644
--- a/gdb/gdbserver/tracepoint.c
+++ b/gdb/gdbserver/tracepoint.c
@@ -29,6 +29,7 @@
#include <stdint.h>

#include "ax.h"
+#include "tdesc.h"

#define DEFAULT_TRACE_BUFFER_SIZE 5242880 /* 5*1024*1024 */

@@ -4675,6 +4676,14 @@ collect_data_at_step (struct tracepoint_hit_ctx *ctx,

#endif

+#ifdef IN_PROCESS_AGENT
+/* The target description used by the IPA. Given that the IPA library
+ is built for a specific architecture that is loaded into the
+ inferior, there only needs to be one such description per
+ build. */
+const struct target_desc *ipa_tdesc;
+#endif
+
static struct regcache *
get_context_regcache (struct tracepoint_hit_ctx *ctx)
{
@@ -4687,7 +4696,7 @@ get_context_regcache (struct tracepoint_hit_ctx *ctx)
if (!fctx->regcache_initted)
{
fctx->regcache_initted = 1;
- init_register_cache (&fctx->regcache, fctx->regspace);
+ init_register_cache (&fctx->regcache, ipa_tdesc, fctx->regspace);
supply_regblock (&fctx->regcache, NULL);
supply_fast_tracepoint_registers (&fctx->regcache, fctx->regs);
}
@@ -4702,7 +4711,7 @@ get_context_regcache (struct tracepoint_hit_ctx *ctx)
if (!sctx->regcache_initted)
{
sctx->regcache_initted = 1;
- init_register_cache (&sctx->regcache, sctx->regspace);
+ init_register_cache (&sctx->regcache, ipa_tdesc, sctx->regspace);
supply_regblock (&sctx->regcache, NULL);
/* Pass down the tracepoint address, because REGS doesn't
include the PC, but we know what it must have been. */
@@ -4761,13 +4770,15 @@ do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
unsigned char *regspace;
struct regcache tregcache;
struct regcache *context_regcache;
-
+ int regcache_size;

trace_debug ("Want to collect registers");

+ context_regcache = get_context_regcache (ctx);
+ regcache_size = register_cache_size (context_regcache->tdesc);
+
/* Collect all registers for now. */
- regspace = add_traceframe_block (tframe, tpoint,
- 1 + register_cache_size ());
+ regspace = add_traceframe_block (tframe, tpoint, 1 + regcache_size);
if (regspace == NULL)
{
trace_debug ("Trace buffer block allocation failed, skipping");
@@ -4776,11 +4787,10 @@ do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
/* Identify a register block. */
*regspace = 'R';

- context_regcache = get_context_regcache (ctx);
-
/* Wrap the regblock in a register cache (in the stack, we
don't want to malloc here). */
- init_register_cache (&tregcache, regspace + 1);
+ init_register_cache (&tregcache, context_regcache->tdesc,
+ regspace + 1);

/* Copy the register data to the regblock. */
regcache_cpy (&tregcache, context_regcache);
@@ -5083,7 +5093,7 @@ traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
{
case 'R':
/* Skip over the registers block. */
- dataptr += register_cache_size ();
+ dataptr += current_target_desc ()->registers_size;
break;
case 'M':
/* Skip over the memory block. */
@@ -5178,12 +5188,13 @@ traceframe_get_pc (struct traceframe *tframe)
{
struct regcache regcache;
unsigned char *dataptr;
+ const struct target_desc *tdesc = current_target_desc ();

dataptr = traceframe_find_regblock (tframe, -1);
if (dataptr == NULL)
return 0;

- init_register_cache (&regcache, dataptr);
+ init_register_cache (&regcache, tdesc, dataptr);
return regcache_read_pc (&regcache);
}

@@ -5737,7 +5748,7 @@ gdb_collect (struct tracepoint *tpoint, unsigned char *regs)
ctx.regcache_initted = 0;
/* Wrap the regblock in a register cache (in the stack, we don't
want to malloc here). */
- ctx.regspace = alloca (register_cache_size ());
+ ctx.regspace = alloca (ipa_tdesc->registers_size);
if (ctx.regspace == NULL)
{
trace_debug ("Trace buffer block allocation failed, skipping");
@@ -6597,7 +6608,7 @@ gdb_probe (const struct marker *mdata, void *probe_private,

/* Wrap the regblock in a register cache (in the stack, we don't
want to malloc here). */
- ctx.regspace = alloca (register_cache_size ());
+ ctx.regspace = alloca (ipa_tdesc->registers_size);
if (ctx.regspace == NULL)
{
trace_debug ("Trace buffer block allocation failed, skipping");
diff --git a/gdb/regformats/regdat.sh b/gdb/regformats/regdat.sh
index be4e01e..6256125 100755
--- a/gdb/regformats/regdat.sh
+++ b/gdb/regformats/regdat.sh
@@ -121,6 +121,7 @@ exec > new-$2
copyright $1
echo '#include "server.h"'
echo '#include "regdef.h"'
+echo '#include "tdesc.h"'
echo
offset=0
i=0
@@ -134,7 +135,7 @@ while do_read
do
if test "${type}" = "name"; then
name="${entry}"
- echo "struct reg regs_${name}[] = {"
+ echo "static struct reg regs_${name}[] = {"
continue
elif test "${type}" = "xmltarget"; then
xmltarget="${entry}"
@@ -160,12 +161,12 @@ done

echo "};"
echo
-echo "const char *expedite_regs_${name}[] = { \"`echo ${expedite} | sed 's/,/", "/g'`\", 0 };"
+echo "static const char *expedite_regs_${name}[] = { \"`echo ${expedite} | sed 's/,/", "/g'`\", 0 };"
if test "${xmltarget}" = x; then
if test "${xmlarch}" = x && test "${xmlosabi}" = x; then
- echo "const char *xmltarget_${name} = 0;"
+ echo "static const char *xmltarget_${name} = 0;"
else
- echo "const char *xmltarget_${name} = \"@<target>\\"
+ echo "static const char *xmltarget_${name} = \"@<target>\\"
if test "${xmlarch}" != x; then
echo "<architecture>${xmlarch}</architecture>\\"
fi
@@ -175,18 +176,27 @@ if test "${xmltarget}" = x; then
echo "</target>\";"
fi
else
- echo "const char *xmltarget_${name} = \"${xmltarget}\";"
+ echo "static const char *xmltarget_${name} = \"${xmltarget}\";"
fi
echo

cat <<EOF
+const struct target_desc *tdesc_${name};
+
void
-init_registers_${name} ()
+init_registers_${name} (void)
{
- set_register_cache (regs_${name},
- sizeof (regs_${name}) / sizeof (regs_${name}[0]));
- gdbserver_expedite_regs = expedite_regs_${name};
- gdbserver_xmltarget = xmltarget_${name};
+ static struct target_desc tdesc_${name}_s;
+ struct target_desc *result = &tdesc_${name}_s;
+
+ result->reg_defs = regs_${name};
+ result->num_registers = sizeof (regs_${name}) / sizeof (regs_${name}[0]);
+ result->expedite_regs = expedite_regs_${name};
+ result->xmltarget = xmltarget_${name};
+
+ init_target_desc (result);
+
+ tdesc_${name} = result;
}
EOF