This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 02/20] Multi-process + multiarch: GDBserver (and GNU/Linux x86* port)


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.

The previous patch 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 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 the 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
(this is very much like what GDB does), and then the arch_setup
routine makes 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 were
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.

The patch adjusts the x86_64 GNU/Linux port, and so the test added in
the previous patch now passes against (extended-remote) GDBserver too.

All other GDBserver ports get broken by this patch.  The following
patches fix each of those, one per patch.  I've tested all that I
could find a machine to test on.  A few were cross-compile tested only
(for I have no access to such machine), and a few were not tested at
all (so it'd quite possible that I broke them, though any fix should
be trivial).

I just 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'.

2012-04-10  Pedro Alves  <palves@redhat.com>

	* Makefile.in (OBS): Add tdesc.o.
	(IPA_OBJS): Add tdesc-ipa.o.
	(tdesc_h): Define.
	(tracepoint-ipa.o, remote-utils-ipa.o, regcache-ipa.o): Depend on
	$tdesc_h.
	(tdesc-ipa.o): New rule.
	(regcache.o, remote-utils.o, server.o): Depend on $tdesc_h.
	(tdesc.o): New rule.
	(tracepoint.o): Depend on $tdesc_h.
	(linux-x86-low.o): Depend on $tdesc_h.
	* 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): 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.
	(i386_cannot_fetch_register): Rename to ...
	(x86_cannot_fetch_register): ... this.  Use is_64bit.
	(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.
	[__x86_64__] (tdesc_amd64_linux, tdesc_amd64_avx_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_is_64bit): 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.
	(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.
	* tdesc.h: New.
	* 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.

	* regformats/regdat.sh <.h output>: Output #include tdesc.h.
	<.c output>: Output a global target description pointer.
	<.c output> (init_registers_${name}): Adjust to initialize a
	target description structure.
---
 gdb/gdbserver/Makefile.in       |   24 +-
 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 |    8 -
 gdb/gdbserver/linux-i386-ipa.c  |    4 
 gdb/gdbserver/linux-low.c       |  209 +++++++++++--------
 gdb/gdbserver/linux-low.h       |   65 +++++-
 gdb/gdbserver/linux-x86-low.c   |  428 ++++++++++++++++++++++++++-------------
 gdb/gdbserver/proc-service.c    |   10 +
 gdb/gdbserver/regcache.c        |  244 ++++++++++++----------
 gdb/gdbserver/regcache.h        |   33 ++-
 gdb/gdbserver/remote-utils.c    |    9 -
 gdb/gdbserver/server.c          |   24 +-
 gdb/gdbserver/server.h          |    6 +
 gdb/gdbserver/tdesc.c           |   66 ++++++
 gdb/gdbserver/tdesc.h           |   63 ++++++
 gdb/gdbserver/tracepoint.c      |   32 ++-
 gdb/regformats/regdat.sh        |   20 +-
 20 files changed, 855 insertions(+), 429 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 6d77ce2..73b33b1 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -138,7 +138,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o targ
 	utils.o version.o \
 	mem-break.o hostio.o event-loop.o tracepoint.o \
 	xml-utils.o common-utils.o ptid.o buffer.o \
-	dll.o \
+	dll.o tdesc.o \
 	$(XML_BUILTIN) \
 	$(DEPFILES) $(LIBOBJS)
 GDBREPLAY_OBS = gdbreplay.o version.o
@@ -237,7 +237,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 utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
+IPA_OBJS=ax-ipa.o tracepoint-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
 
@@ -339,6 +339,7 @@ ptid_h = $(srcdir)/../common/ptid.h
 ax_h = $(srcdir)/ax.h
 agent_h = $(srcdir)/../common/agent.h
 linux_osdata_h = $(srcdir)/../common/linux-osdata.h
+tdesc_h = $(srcdir)/tdesc.h
 server_h = $(srcdir)/server.h $(regcache_h) config.h $(srcdir)/target.h \
 		$(srcdir)/mem-break.h $(srcdir)/../common/gdb_signals.h \
 		$(srcdir)/../common/common-utils.h \
@@ -372,15 +373,15 @@ IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_CFLAGS) $(UST_CFLAGS) \
 # In-process agent object rules
 ax-ipa.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o ax-ipa.o
-tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h}
+tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h} ${tdesc_h}
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o
 utils-ipa.o: utils.c $(server_h)
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o
 common-utils-ipa.o: ../common/common-utils.c $(server_h)
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o common-utils-ipa.o
-remote-utils-ipa.o: remote-utils.c $(server_h)
+remote-utils-ipa.o: remote-utils.c $(server_h) $(tdesc_h)
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o remote-utils-ipa.o
-regcache-ipa.o: regcache.c $(server_h)
+regcache-ipa.o: regcache.c $(server_h) ${tdesc_h}
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o regcache-ipa.o
 i386-linux-ipa.o : i386-linux.c $(regdef_h)
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o i386-linux-ipa.o
@@ -390,6 +391,8 @@ linux-amd64-ipa.o: linux-amd64-ipa.c $(server_h)
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o linux-amd64-ipa.o
 amd64-linux-ipa.o : amd64-linux.c $(regdef_h)
 	$(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o
+tdesc-ipa.o: tdesc.c $(server_h) ${regdef_h} ${tdesc_h}
+	$(CC) -c $(IPAGENT_CFLAGS) $< -o tdesc-ipa.o
 
 ax.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
 event-loop.o: event-loop.c $(server_h)
@@ -398,13 +401,14 @@ hostio-errno.o: hostio-errno.c $(server_h)
 inferiors.o: inferiors.c $(server_h)
 mem-break.o: mem-break.c $(server_h) $(ax_h)
 proc-service.o: proc-service.c $(server_h) $(gdb_proc_service_h)
-regcache.o: regcache.c $(server_h) $(regdef_h)
-remote-utils.o: remote-utils.c terminal.h $(server_h)
-server.o: server.c $(server_h) $(agent_h)
+regcache.o: regcache.c $(server_h) $(regdef_h) ${tdesc_h}
+remote-utils.o: remote-utils.c terminal.h $(server_h) $(tdesc_h)
+server.o: server.c $(server_h) $(agent_h) $(tdesc_h)
 target.o: target.c $(server_h)
+tdesc.o: tdesc.c $(server_h) $(regdef_h) ${tdesc_h}
 thread-db.o: thread-db.c $(server_h) $(linux_low_h) $(gdb_proc_service_h) \
 	$(gdb_thread_db_h)
-tracepoint.o: tracepoint.c $(server_h) $(ax_h) $(agent_h)
+tracepoint.o: tracepoint.c $(server_h) $(ax_h) $(agent_h) ${tdesc_h}
 utils.o: utils.c $(server_h)
 gdbreplay.o: gdbreplay.c config.h
 dll.o: dll.c $(server_h)
@@ -477,7 +481,7 @@ linux-s390-low.o: linux-s390-low.c $(linux_low_h) $(server_h)
 linux-sh-low.o: linux-sh-low.c $(linux_low_h) $(server_h)
 linux-tic6x-low.o: linux-tic6x-low.c $(linux_low_h) $(server_h)
 linux-x86-low.o: linux-x86-low.c $(linux_low_h) $(server_h) \
-	$(gdb_proc_service_h) $(i386_low_h) $(agent_h)
+	$(gdb_proc_service_h) $(i386_low_h) $(agent_h) $(tdesc_h)
 linux-xtensa-low.o: linux-xtensa-low.c xtensa-xtregs.c $(linux_low_h) $(server_h)
 
 lynx-low.o: lynx-low.c $(server_h) $(target_h) $(lynx_low_h)
diff --git a/gdb/gdbserver/ax.c b/gdb/gdbserver/ax.c
index 4eba27b..ac7cddb 100644
--- a/gdb/gdbserver/ax.c
+++ b/gdb/gdbserver/ax.c
@@ -1043,7 +1043,7 @@ gdb_eval_agent_expr (struct regcache *regcache,
 	  {
 	    int regnum = arg;
 
-	    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 10e1bc1..79d9084 100644
--- a/gdb/gdbserver/i387-fp.c
+++ b/gdb/gdbserver/i387-fp.c
@@ -21,8 +21,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.  */
 
@@ -118,7 +116,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++)
@@ -158,7 +156,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++)
@@ -194,9 +192,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,
@@ -250,6 +250,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.  */
@@ -275,7 +277,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++)
 	{
@@ -292,7 +294,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++) 
 	{
@@ -309,7 +311,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++) 
 	{
@@ -414,9 +416,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,
@@ -469,6 +473,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.  */
@@ -477,7 +483,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)
 	{
@@ -494,7 +500,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))
 	{
@@ -511,7 +517,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 eb60342..c7b22e7 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 2b9169a..94eb54c 100644
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -101,7 +101,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 7df5aeb..1720a07 100644
--- a/gdb/gdbserver/linux-amd64-ipa.c
+++ b/gdb/gdbserver/linux-amd64-ipa.c
@@ -23,6 +23,9 @@
 /* Defined in auto-generated file amd64-linux.c.  */
 void init_registers_amd64_linux (void);
 
+/* Ditto.  */
+extern struct target_desc *tdesc_amd64_linux;
+
 /* fast tracepoints collect registers.  */
 
 #define FT_CR_RIP 0
@@ -164,12 +167,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 1eb59a0..e6b8f53 100644
--- a/gdb/gdbserver/linux-i386-ipa.c
+++ b/gdb/gdbserver/linux-i386-ipa.c
@@ -50,6 +50,9 @@ enum i386_gdb_regnum
 /* Defined in auto-generated file i386-linux.c.  */
 void init_registers_i386_linux (void);
 
+/* Ditto.  */
+extern struct target_desc *tdesc_i386_linux;
+
 #define FT_CR_EAX 15
 #define FT_CR_ECX 14
 #define FT_CR_EDX 13
@@ -250,5 +253,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 043451d..7657e26 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -182,15 +182,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);
@@ -245,11 +236,6 @@ struct pending_signals
 #define PTRACE_ARG4_TYPE void *
 #define PTRACE_XFER_TYPE long
 
-#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 };
@@ -325,13 +311,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 ();
 
@@ -1142,8 +1127,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);
@@ -1350,17 +1334,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
@@ -3254,8 +3249,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;
@@ -3957,14 +3951,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)
@@ -3972,7 +3967,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;
@@ -3999,9 +3995,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;
 	    }
@@ -4026,14 +4025,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)
@@ -4041,7 +4041,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;
@@ -4086,9 +4087,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;
 	    }
@@ -4120,8 +4124,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
 
@@ -4129,50 +4133,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);
 
@@ -4198,23 +4204,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);
@@ -4255,16 +4263,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.
@@ -4273,22 +4284,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
 
@@ -4298,15 +4312,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
     {
@@ -4314,11 +4331,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);
     }
 }
 
@@ -4327,19 +4345,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);
     }
 }
 
@@ -5767,6 +5789,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)
 {
@@ -5777,14 +5811,11 @@ initialize_low (void)
 		       the_low_target.breakpoint_len);
   linux_init_signals ();
   linux_test_for_tracefork ();
-#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 07eda12..1115f1c 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -44,8 +44,51 @@ 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
+{
+  struct regset_info *regsets;
+  int num_regsets;
+  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
 {
@@ -58,6 +101,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;
@@ -67,14 +115,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
@@ -284,6 +325,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 7feb721..e9583e2 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -30,6 +30,7 @@
 
 #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);
@@ -156,6 +157,21 @@ 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 is a 64-bit process.  */
+
+static int
+is_64bit (void)
+{
+  struct regcache *regcache = get_thread_regcache (current_inferior, 0);
+
+  return register_size (regcache->tdesc, 0) == 8;
+}
+
+#endif
+
 
 /* Called by libthread_db.  */
 
@@ -164,7 +180,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 ();
 
   if (use_64bit)
     {
@@ -206,7 +222,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 ();
 
   if (use_64bit)
     {
@@ -246,14 +262,24 @@ x86_get_thread_area (int lwpid, CORE_ADDR *addr)
 
 
 static int
-i386_cannot_store_register (int regno)
+x86_cannot_store_register (int regno)
 {
+#ifdef __x86_64__
+  if (is_64bit ())
+    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 ())
+    return 0;
+#endif
+
   return regno >= I386_NUM_REGS;
 }
 
@@ -263,7 +289,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)
@@ -285,7 +311,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,7 +384,7 @@ x86_store_xstateregset (struct regcache *regcache, const void *buf)
    to linux_target_ops and set the right one there, rather than having to
    modify the target_regsets global.  */
 
-struct regset_info target_regsets[] =
+static struct regset_info x86_regsets[] =
 {
 #ifdef HAVE_PTRACE_GETREGS
   { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
@@ -383,7 +409,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)
     {
@@ -402,7 +428,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)
     {
@@ -922,7 +948,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 ())
     {
       if (sizeof (siginfo_t) != sizeof (compat_siginfo_t))
 	fatal ("unexpected difference in siginfo");
@@ -941,136 +967,221 @@ x86_siginfo_fixup (siginfo_t *native, void *inf, int direction)
 
 static int use_xml;
 
-/* Update gdbserver_xmltarget.  */
+#ifdef __x86_64__
+extern struct target_desc *tdesc_amd64_linux;
+extern struct target_desc *tdesc_amd64_avx_linux;
+#endif
 
-static void
-x86_linux_update_xmltarget (void)
-{
-  int pid;
-  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;
+extern struct target_desc *tdesc_i386_linux;
+extern struct target_desc *tdesc_i386_mmx_linux;
+extern 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;
+
+/* 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
+;
+
+/* Does the current host support PTRACE_GETREGSET?  */
+static int have_ptrace_getregset = -1;
 
-  if (!current_inferior)
-    return;
 
-  /* 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 ();
+/* Get Linux/x86 target description from running target.
 
-  pid = pid_of (get_thread_lwp (current_inferior));
+   Value of CS segment register:
+     1. 64bit process: 0x33.
+     2. 32bit process: 0x23.
+ */
+
+#define AMD64_LINUX_USER64_CS	0x33
+
+static int
+x86_linux_is_64bit (int tid)
+{
 #ifdef __x86_64__
-  if (num_xmm_registers == 8)
-    init_registers_i386_linux ();
-  else
-    init_registers_amd64_linux ();
+  unsigned long cs;
+
+  /* Get CS register.  */
+  errno = 0;
+  cs = ptrace (PTRACE_PEEKUSER, tid,
+	       offsetof (struct user_regs_struct, cs), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't get CS register"));
+
+  return cs == AMD64_LINUX_USER64_CS;
 #else
-    {
-# ifdef HAVE_PTRACE_GETFPXREGS
-      if (have_ptrace_getfpxregs == -1)
-	{
-	  elf_fpxregset_t fpxregs;
+  return 0;
+#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;
-	}
+static struct target_desc *
+x86_linux_read_description (void)
+{
+  struct regset_info *regset;
+  int avx;
+  int tid;
+  int is_64bit;
+  static uint64_t xcr0;
+
+  tid = lwpid_of (get_thread_lwp (current_inferior));
+  is_64bit = x86_linux_is_64bit (tid);
+
+#if !defined __x86_64__ && defined HAVE_PTRACE_GETFPXREGS
+  if (!is_64bit && 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 (is_64bit)
+	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;
 
 #ifdef __x86_64__
-	  /* I386 has 8 xmm regs.  */
-	  if (num_xmm_registers == 8)
-	    init_registers_i386_avx_linux ();
-	  else
-	    init_registers_amd64_avx_linux ();
-#else
-	  init_registers_i386_avx_linux ();
+  if (is_64bit)
+    {
+      if (avx)
+	return tdesc_amd64_avx_linux;
+      else
+	return tdesc_amd64_linux;
+    }
+  else
 #endif
-	}
+    {
+      if (avx)
+	return tdesc_i386_avx_linux;
+      else
+	return tdesc_i386_linux;
     }
 }
 
+/* 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;
+
+  for_each_inferior (&all_processes, x86_arch_setup_process_callback);
+
+  current_inferior = save_inferior;
+}
+
 /* Process qSupported query, "xmlRegisters=".  Update the buffer size for
    PTRACE_GETREGSET.  */
 
@@ -1101,48 +1212,60 @@ x86_linux_process_qsupported (const char *query)
   x86_linux_update_xmltarget ();
 }
 
-/* Initialize gdbserver for the architecture of the inferior.  */
+/* Common for 32-bit/64-bit.  */
+
+static struct regsets_info x86_regsets_info =
+  {
+    x86_regsets, /* regsets */
+    0, /* num_regsets */
+    NULL, /* disabled_regsets */
+  };
 
-static void
-x86_arch_setup (void)
-{
 #ifdef __x86_64__
-  int pid = pid_of (get_thread_lwp (current_inferior));
-  int use_64bit = linux_pid_exe_is_elf_64_file (pid);
+static struct regs_info amd64_linux_regs_info =
+  {
+    NULL, /* regset_bitmap */
+    NULL, /* usrregs_info */
+    &x86_regsets_info
+  };
+#endif
 
-  if (use_64bit < 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 (use_64bit)
-    {
-      /* 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;
+static struct usrregs_info i386_linux_usrregs_info =
+  {
+    I386_NUM_REGS,
+    i386_regmap,
+  };
 
-      /* Amd64 has 16 xmm regs.  */
-      num_xmm_registers = 16;
+static struct regs_info i386_linux_regs_info =
+  {
+    NULL, /* regset_bitmap */
+    &i386_linux_usrregs_info,
+    &x86_regsets_info
+  };
 
-      x86_linux_update_xmltarget ();
-      return;
-    }
+const struct regs_info *
+x86_linux_regs_info (void)
+{
+#ifdef __x86_64__
+  if (is_64bit ())
+    return &amd64_linux_regs_info;
+  else
 #endif
+    return &i386_linux_regs_info;
+}
 
-  /* Ok we have a 32-bit inferior.  */
-
-  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;
+/* Initialize the target description for the architecture of the
+   inferior.  */
 
-  /* I386 has 8 xmm regs.  */
-  num_xmm_registers = 8;
+static void
+x86_arch_setup (void)
+{
+  /* Before changing the register cache internal layout, flush the
+     contents of the current valid caches back to the threads, and
+     release the current regcache objects.  */
+  regcache_release ();
 
-  x86_linux_update_xmltarget ();
+  current_process ()->tdesc = x86_linux_read_description ();
 }
 
 static int
@@ -1572,7 +1695,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 ())
     return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
 						   collector, lockaddr,
 						   orig_size, jump_entry,
@@ -1606,7 +1729,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 ())
     return 5;
 #endif
 
@@ -2949,9 +3072,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 ())
     return &amd64_emit_ops;
   else
 #endif
@@ -2964,11 +3085,9 @@ x86_emit_ops (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,
@@ -2998,3 +3117,26 @@ struct linux_target_ops the_low_target =
   x86_emit_ops,
   x86_get_min_fast_tracepoint_insn_len,
 };
+
+void
+initialize_low_arch (void)
+{
+  /* Initialize the Linux target descriptions.  */
+#ifdef __x86_64__
+  init_registers_amd64_linux ();
+  init_registers_amd64_avx_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 5584dab..ccb026f 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 5eda9df..150e250 100644
--- a/gdb/gdbserver/regcache.c
+++ b/gdb/gdbserver/regcache.c
@@ -19,17 +19,11 @@
 
 #include "server.h"
 #include "regdef.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,
+		     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 (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;
+  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,106 @@ registers_from_string (struct regcache *regcache, char *buf)
 {
   int len = strlen (buf);
   unsigned char *registers = regcache->registers;
+  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 (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 (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 (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 int
+free_register_cache_thread_one (struct inferior_list_entry *entry,
+				void *pid_p)
+{
+  struct thread_info *thread = (struct thread_info *) entry;
+  int pid = *(int *) pid_p;
+
+  /* Only release the regcaches of threads of this process.  */
+  if (ptid_get_pid (entry->id) == pid)
+    free_register_cache_thread (thread);
+
+  return 0;
+}
+
+void
+regcache_release (void)
+{
+  /* Only free regcaches of threads of the current process.  */
+  int pid = ptid_get_pid (current_inferior->entry.id);
+
+  /* Re-allocate all pre-existing register caches.  */
+  find_inferior (&all_threads, free_register_cache_thread_one, &pid);
+}
+#endif
+
 int
-register_size (int n)
+register_cache_size (struct target_desc *tdesc)
 {
-  return reg_defs[n].size / 8;
+  return tdesc->registers_size;
+}
+
+int
+register_size (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 +329,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 +338,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 +352,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 +369,28 @@ supply_regblock (struct regcache *regcache, const void *buf)
 {
   if (buf)
     {
-      memcpy (regcache->registers, buf, register_bytes);
+      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);
+      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 +403,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 +411,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 +420,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 d52012c..9ff6213 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.  */
+  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,
+				      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 (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 (struct target_desc *tdesc, int n);
 
-int register_cache_size (void);
+int register_cache_size (struct target_desc *tdesc);
 
-int find_regno (const char *name);
+int register_size (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 (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 4e35bb7..50a9263 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -19,6 +19,7 @@
 #include "server.h"
 #include "terminal.h"
 #include "target.h"
+#include "tdesc.h"
 #include <stdio.h>
 #include <string.h>
 #if HAVE_SYS_IOCTL_H
@@ -1269,7 +1270,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;
@@ -1327,12 +1328,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 = get_desc ()->expedite_regs;
+
 	regcache = get_thread_regcache (current_inferior, 1);
 
 	if (the_target->stopped_by_watchpoint != NULL
@@ -1357,7 +1358,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 9c7159f..14dca67 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -19,6 +19,7 @@
 
 #include "server.h"
 #include "agent.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
@@ -607,21 +606,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".
+  struct target_desc *desc = get_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
@@ -3154,7 +3154,7 @@ process_serial_event (void)
       require_running (own_buf);
       if (current_traceframe >= 0)
 	{
-	  struct regcache *regcache = new_register_cache ();
+	  struct regcache *regcache = new_register_cache (get_desc ());
 
 	  if (fetch_traceframe_registers (current_traceframe,
 					  regcache, -1) == 0)
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index a419c36..1c52fec 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -139,6 +139,7 @@ struct inferior_list_entry
 struct thread_info;
 struct process_info;
 struct regcache;
+struct target_desc;
 
 #include "regcache.h"
 #include "gdb/signals.h"
@@ -221,6 +222,8 @@ struct process_info
   /* The list of installed fast tracepoints.  */
   struct fast_tracepoint_jump *fast_tracepoint_jumps;
 
+  struct target_desc *tdesc;
+
   /* Private target data.  */
   struct process_info_private *private;
 };
@@ -508,6 +511,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 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..a278217
--- /dev/null
+++ b/gdb/gdbserver/tdesc.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2012 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
+
+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;
+}
+
+struct target_desc *
+get_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..eff30de
--- /dev/null
+++ b/gdb/gdbserver/tdesc.h
@@ -0,0 +1,63 @@
+/* Target description definitions for remote server for GDB.
+   Copyright (C) 2012 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;
+
+/* Per-inferior target description info.  */
+
+struct target_desc
+{
+  /* An array of NUM_REGISTERS elements of register definitions that
+     description 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 target description.  */
+
+struct target_desc *get_desc (void);
+
+#endif /* TDESC_H */
diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c
index 33173c6..e475f91 100644
--- a/gdb/gdbserver/tracepoint.c
+++ b/gdb/gdbserver/tracepoint.c
@@ -28,6 +28,7 @@
 #include <stdint.h>
 #endif
 #include "ax.h"
+#include "tdesc.h"
 
 /* This file is built for both GDBserver, and the in-process
    agent (IPA), a shared library that includes a tracing agent that is
@@ -4478,6 +4479,11 @@ collect_data_at_step (struct tracepoint_hit_ctx *ctx,
 
 #endif
 
+#ifdef IN_PROCESS_AGENT
+/* The target description used by the IPA.  */
+struct target_desc *ipa_tdesc;
+#endif
+
 static struct regcache *
 get_context_regcache (struct tracepoint_hit_ctx *ctx)
 {
@@ -4490,7 +4496,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);
 	}
@@ -4505,7 +4511,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.  */
@@ -4560,13 +4566,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,
-					 1 + register_cache_size ());
+	regspace = add_traceframe_block (tframe, 1 + regcache_size);
 	if (regspace == NULL)
 	  {
 	    trace_debug ("Trace buffer block allocation failed, skipping");
@@ -4575,11 +4583,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);
@@ -4887,7 +4894,7 @@ traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
 	{
 	case 'R':
 	  /* Skip over the registers block.  */
-	  dataptr += register_cache_size ();
+	  dataptr += get_desc ()->registers_size;
 	  break;
 	case 'M':
 	  /* Skip over the memory block.  */
@@ -4982,12 +4989,13 @@ traceframe_get_pc (struct traceframe *tframe)
 {
   struct regcache regcache;
   unsigned char *dataptr;
+  struct target_desc *tdesc = get_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);
 }
 
@@ -5538,7 +5546,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");
@@ -6348,7 +6356,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 3ca57fa..58a990d 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
@@ -180,13 +181,22 @@ fi
 echo
 
 cat <<EOF
+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
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]