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]

[rfc] [13/18] Cell multi-arch: Core of the Cell debugger


Hello,

this is the main patch adding support for combined Cell multi-arch debugging.

The main flow of control works as follows:

- The ppc_linux_init_abi checks the target description whether the current
  target actually is a Cell system.  If so, it enables Cell multi-arch support:

   * Install a special version of displaced_step_location that does not rely
     on entry_point_address

   * Install the Cell multi-arch shared library handler

- The multi-arch shared library handler (implemented in solib-spu.c) is
  layered on top of solib-svr4.c.  It recognizes all SVR4 libraries on 
  the PowerPC side as usual.

  In addition, it queries the target (using TARGET_OBJECT_SPU requests)
  to determine all currently active SPU contexts, and the SPU executables
  currently loaded into those contexts.

  Each such executable is installed as a shared library to the current
  process.  The objfile is relocated to the GDB-internal address range
  corresponding to the SPU ID of the given context.

  As soon as any such shared library is present, the solib-spu layer
  pushes a new target layer to handle references to SPU contexts.

- This layer is implemented in spu-multiarch.c as a target using the new
  "arch_stratum" layer, which sits on top of thread_stratum.

  It is used to dispatch multi-architecture requests to the appropriate
  target mechanism:

   * SPU registers are accessed via TARGET_OBJECT_SPU requests on the
     spufs "regs" file, or accesses to PPU memory (the PC).

   * Memory accesses are dispatched according to the GDB internal address;
     SPU memory is accessed via TARGET_OBJECT_SPU requests on the
     spufs "mem" file

   * Breakpoints are dispatched according  to the GDB internal address

  In addition, this is the target layer that implements the
  to_target_architecture callback to determine whether the target is
  currently stopped in PowerPC or SPU code.

- Finally, the SPU tdep layer is updated to handle GDB internal addresses
  that encode SPU IDs.  The ID is actually stored within the gdbarch itself,
  so we have a different gdbarch object for each active SPU context.

  This allows the pointer/integer to address routines to encode the 
  SPU ID used for the current context into the GDB internal address.
  Several other callbacks (read_pc, read_sp etc) likewise encode the
  SPU ID; other callback have to handle encoded addresses.

Bye,
Ulrich


ChangeLog:

	* target.h (enum strata): New value arch_stratum.
	* target.c (target_require_runnable): Skip arch_stratum targets.

	* configure.tgt (powerpc-*-linux* | powerpc64-*-linux*): Add
	solib-spu.o and spu-multiarch.o to gdb_target_obs.
	* Makefile.in (ALL_TARGET_OBS): Add solib-spu.o and spu-multiarch.o.
	(ALLDEPFILES): Add solib-spu.c and spu-multiarch.c.

	* solib-spu.c: New file.
	* spu-multiarch.c: New file.

	* spu-tdep.h (SPUADDR, SPUADDR_SPU, SPUADDR_ADDR): New macros.
	(set_spu_solib_ops): Add prototype.
	(spu_multiarch_enable, spu_multiarch_disable): Likewise.

	* spu-tdep.c (struct gdbarch_tdep): New members id and lslr.
	(spu_lslr): New function.

	(spu_address_to_pointer): New function.
	(spu_pointer_to_address): Support SPU ID address encoding.  Use
	lslr from tdep structure.
	(spu_integer_to_address): Likewise.
	(spu_frame_unwind_cache): Update for encoded addresses.
	(spu_unwind_pc, spu_unwind_sp): Likewise.
	(spu_read_pc, spu_write_pc): Likewise.
	(spu_push_dummy_call): Likewise.
	(spu_software_single_step): Likewise.
	(spu_overlay_update_osect): Likewise.

	(spu_dis_asm_print_address): New function.
	(gdb_print_insn_spu): Likewise.

	(spu_gdbarch_init): Store requested SPU ID in tdep structure.
	Query for LSLR and store it into tdep as well.
	Install spu_address_to_pointer and gdb_print_insn_spu.

	* ppc-linux-tdep.c: Include "observer.h", "auxv.h" and "elf/common.h".
	(ppc_linux_entry_point_addr): New static variable.
	(ppc_linux_inferior_created): New function.
	(ppc_linux_displaced_step_location): Likewise.
	(ppc_linux_init_abi): Enable Cell/B.E. support if supported
	by the target.
	(_initialize_ppc_linux_tdep): Attach to inferior_created observer.

testsuite/ChangeLog:

	* gdb.xml/tdesc-regs.exp: Skip for SPU targets.


Index: src/gdb/configure.tgt
===================================================================
--- src.orig/gdb/configure.tgt
+++ src/gdb/configure.tgt
@@ -349,7 +349,8 @@ powerpc-*-aix* | rs6000-*-*)
 powerpc-*-linux* | powerpc64-*-linux*)
 	# Target: PowerPC running Linux
 	gdb_target_obs="rs6000-tdep.o ppc-linux-tdep.o ppc-sysv-tdep.o \
-			solib.o solib-svr4.o corelow.o symfile-mem.o"
+			solib.o solib-svr4.o solib-spu.o spu-multiarch.o \
+			corelow.o symfile-mem.o"
 	gdb_sim=../sim/ppc/libsim.a
 	build_gdbserver=yes
 	;;
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in
+++ src/gdb/Makefile.in
@@ -498,7 +498,7 @@ ALL_TARGET_OBS = \
 	sh64-tdep.o sh-linux-tdep.o shnbsd-tdep.o sh-tdep.o \
 	sparc-linux-tdep.o sparcnbsd-tdep.o sparcobsd-tdep.o \
 	sparc-sol2-tdep.o sparc-tdep.o \
-	spu-tdep.o \
+	spu-tdep.o spu-multiarch.o solib-spu.o \
 	v850-tdep.o \
 	vaxnbsd-tdep.o vaxobsd-tdep.o vax-tdep.o \
 	xstormy16-tdep.o \
@@ -1331,7 +1331,7 @@ ALLDEPFILES = \
 	sparc64-tdep.c sparc64fbsd-nat.c sparc64fbsd-tdep.c \
 	sparc64nbsd-nat.c sparc64nbsd-tdep.c sparc64obsd-tdep.c \
 	sparcnbsd-nat.c sparcnbsd-tdep.c sparcobsd-tdep.c \
-	spu-linux-nat.c spu-tdep.c \
+	spu-linux-nat.c spu-tdep.c spu-multiarch.c solib-spu.c \
 	v850-tdep.c \
 	vax-nat.c vax-tdep.c vaxbsd-nat.c vaxnbsd-tdep.c \
 	win32-nat.c \
Index: src/gdb/ppc-linux-tdep.c
===================================================================
--- src.orig/gdb/ppc-linux-tdep.c
+++ src/gdb/ppc-linux-tdep.c
@@ -38,6 +38,9 @@
 #include "trad-frame.h"
 #include "frame-unwind.h"
 #include "tramp-frame.h"
+#include "observer.h"
+#include "auxv.h"
+#include "elf/common.h"
 
 #include "features/rs6000/powerpc-32l.c"
 #include "features/rs6000/powerpc-altivec32l.c"
@@ -991,6 +994,46 @@ static struct tramp_frame ppc64_linux_si
 };
 
 
+/* Address to use for displaced stepping.  When debugging a stand-alone
+   SPU executable, entry_point_address () will point to an SPU local-store
+   address and is thus not usable as displaced stepping location.  We use
+   the auxiliary vector to determine the PowerPC-side entry point address
+   instead.  */
+
+static CORE_ADDR ppc_linux_entry_point_addr = 0;
+
+static void
+ppc_linux_inferior_created (struct target_ops *target, int from_tty)
+{
+  ppc_linux_entry_point_addr = 0;
+}
+
+static CORE_ADDR
+ppc_linux_displaced_step_location (struct gdbarch *gdbarch)
+{
+  if (ppc_linux_entry_point_addr == 0)
+    {
+      CORE_ADDR addr;
+
+      /* Determine entry point from target auxiliary vector.  */
+      if (target_auxv_search (&current_target, AT_ENTRY, &addr) <= 0)
+	error (_("Cannot find AT_ENTRY auxiliary vector entry."));
+
+      /* Make certain that the address points at real code, and not a
+	 function descriptor.  */
+      addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
+						 &current_target);
+
+      /* Inferior calls also use the entry point as a breakpoint location.
+	 We don't want displaced stepping to interfere with those
+	 breakpoints, so leave space.  */
+      ppc_linux_entry_point_addr = addr + 2 * PPC_INSN_SIZE;
+    }
+
+  return ppc_linux_entry_point_addr;
+}
+
+
 /* Return 1 if PPC_ORIG_R3_REGNUM and PPC_TRAP_REGNUM are usable.  */
 int
 ppc_linux_trap_reg_p (struct gdbarch *gdbarch)
@@ -1177,6 +1220,19 @@ ppc_linux_init_abi (struct gdbarch_info 
 				   PPC_TRAP_REGNUM, "trap");
 	}
     }
+
+  /* Enable Cell/B.E. if supported by the target.  */
+  if (tdesc_compatible_p (info.target_desc,
+			  bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu)))
+    {
+      /* Cell/B.E. multi-architecture support.  */
+      set_spu_solib_ops (gdbarch);
+
+      /* The default displaced_step_at_entry_point doesn't work for
+	 SPU stand-alone executables.  */
+      set_gdbarch_displaced_step_location (gdbarch,
+					   ppc_linux_displaced_step_location);
+    }
 }
 
 void
@@ -1191,6 +1247,9 @@ _initialize_ppc_linux_tdep (void)
   gdbarch_register_osabi (bfd_arch_rs6000, bfd_mach_rs6k, GDB_OSABI_LINUX,
                          ppc_linux_init_abi);
 
+  /* Attach to inferior_created observer.  */
+  observer_attach_inferior_created (ppc_linux_inferior_created);
+
   /* Initialize the Linux target descriptions.  */
   initialize_tdesc_powerpc_32l ();
   initialize_tdesc_powerpc_altivec32l ();
Index: src/gdb/solib-spu.c
===================================================================
--- /dev/null
+++ src/gdb/solib-spu.c
@@ -0,0 +1,477 @@
+/* Cell SPU GNU/Linux support -- shared library handling.
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include "gdb_assert.h"
+#include "gdb_stat.h"
+#include "arch-utils.h"
+#include "bfd.h"
+#include "symtab.h"
+#include "solib.h"
+#include "solib-svr4.h"
+#include "solist.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "observer.h"
+
+#include "spu-tdep.h"
+
+/* Highest SPE id (file handle) the inferior may have.  */
+#define MAX_SPE_FD 1024
+
+/* Stand-alone SPE executable?  */
+#define spu_standalone_p() \
+  (symfile_objfile && symfile_objfile->obfd \
+   && bfd_get_arch (symfile_objfile->obfd) == bfd_arch_spu)
+
+
+/* Relocate main SPE executable.  */
+static void spu_relocate_main_executable (int spufs_fd)
+{
+  struct objfile *objfile;
+  struct cleanup *old_chain;
+  struct section_offsets *new_offsets;
+  int i;
+
+  for (objfile = symfile_objfile;
+       objfile;
+       objfile = objfile->separate_debug_objfile)
+    {
+      new_offsets = xcalloc (objfile->num_sections,
+			     sizeof (struct section_offsets));
+      old_chain = make_cleanup (xfree, new_offsets);
+
+      for (i = 0; i < objfile->num_sections; i++)
+        new_offsets->offsets[i] = SPUADDR (spufs_fd, 0);
+
+      objfile_relocate (objfile, new_offsets);
+      do_cleanups (old_chain);
+    }
+}
+
+/* When running a stand-alone SPE executable, we need to skip one more
+   exec event on startup, to get past the binfmt_misc loader.  */
+static void
+spu_skip_standalone_loader (void)
+{
+  if (target_has_execution)
+    {
+      struct cleanup *old_chain;
+
+      /* Suppress MI messages that are unexpected at this point.  */
+      old_chain = make_cleanup_restore_integer (&suppress_resume_observer);
+      suppress_resume_observer = 1;
+      make_cleanup_restore_integer (&suppress_stop_observer);
+      suppress_stop_observer = 1;
+
+      /* We either may or may not have an extra SIGTRAP pending, depending
+	 on whether we've started a new inferior or attached to one that was
+	 already running.  We cannot readily distinguish between the two,
+	 in particular when using the extended-remote target.  Thus, we pass
+	 TARGET_SIGNAL_TRAP to resume -- this way we will always get a trap.
+	 (If one was already pending, it will be combined into this trap.)  */
+      stop_soon = STOP_QUIETLY;
+      resume (0, TARGET_SIGNAL_TRAP);
+      wait_for_inferior (1);
+      stop_soon = NO_STOP_QUIETLY;
+
+      do_cleanups (old_chain);
+    }
+}
+
+/* Build a list of `struct so_list' objects describing the shared
+   objects currently loaded in the inferior.  */
+static struct so_list *
+spu_current_sos (void)
+{
+  struct so_list *head;
+  struct so_list **link_ptr;
+
+  char buf[MAX_SPE_FD * 4];
+  int i, size;
+
+  /* First, retrieve the SVR4 shared library list.  Switch to the
+     PPE architecture while doing so.  This should not be strictly
+     necessary, but it is right now.  */
+  struct cleanup *old_chain = save_current_gdbarch ();
+  current_gdbarch = target_gdbarch;
+  head = svr4_so_ops.current_sos ();
+  do_cleanups (old_chain);
+
+  /* Append our libraries to the end of the list.  */
+  for (link_ptr = &head; *link_ptr; link_ptr = &(*link_ptr)->next)
+    ;
+
+  /* Determine list of SPU ids.  */
+  size = target_read (&current_target, TARGET_OBJECT_SPU, NULL,
+		      buf, 0, sizeof buf);
+
+  /* Do not add stand-alone SPE executable context as shared library,
+     but relocate main SPE executable objfile.  */
+  if (spu_standalone_p ())
+    {
+      if (size == 4)
+	spu_relocate_main_executable (extract_unsigned_integer (buf, 4));
+
+      return head;
+    }
+
+  /* As soon as any SPE context exists in the inferior, we have to
+     enable SPU multi-architecture target support.  */
+  if (size > 0)
+    spu_multiarch_enable ();
+  else
+    spu_multiarch_disable ();
+
+  /* Create an so_list entry for each SPU id.  */
+  for (i = 0; i < size; i += 4)
+    {
+      int fd = extract_unsigned_integer (buf + i, 4);
+      struct so_list *new;
+
+      unsigned long long addr;
+      char annex[32], id[100];
+      int len;
+
+      /* Read object ID.  There's a race window where the inferior may have
+	 already created the SPE context, but not installed the object-id
+	 yet.  Skip such entries; we'll be back for them later.  */
+      xsnprintf (annex, sizeof annex, "%d/object-id", fd);
+      len = target_read (&current_target, TARGET_OBJECT_SPU, annex,
+			 id, 0, sizeof id);
+      if (len <= 0 || len >= sizeof id)
+	continue;
+      id[len] = 0;
+      if (sscanf (id, "0x%llx", &addr) != 1 || !addr)
+	continue;
+
+      /* Allocate so_list structure.  */
+      new = XZALLOC (struct so_list);
+
+      /* Encode FD and object ID in path name.  Choose the name so as not
+	 to conflict with any (normal) SVR4 library path name.  */
+      xsnprintf (new->so_name, sizeof new->so_name, "@0x%llx <%d>", addr, fd);
+      strcpy (new->so_original_name, new->so_name);
+
+      *link_ptr = new;
+      link_ptr = &new->next;
+    }
+
+  return head;
+}
+
+/* Free so_list information.  */
+static void
+spu_free_so (struct so_list *so)
+{
+  if (so->so_original_name[0] != '@')
+    svr4_so_ops.free_so (so);
+}
+
+/* Relocate section addresses.  */
+static void
+spu_relocate_section_addresses (struct so_list *so,
+				struct section_table *sec)
+{
+  if (so->so_original_name[0] != '@')
+    svr4_so_ops.relocate_section_addresses (so, sec);
+  else
+    {
+      unsigned long long addr;
+      int fd;
+
+      /* Set addr_low/high to just LS offset for display.  */
+      if (so->addr_low == 0 && so->addr_high == 0
+          && strcmp (sec->the_bfd_section->name, ".text") == 0)
+        {
+          so->addr_low = sec->addr;
+          so->addr_high = sec->endaddr;
+        }
+
+      /* Decode object ID.  */
+      if (sscanf (so->so_original_name, "@0x%llx <%d>", &addr, &fd) != 2)
+	internal_error (__FILE__, __LINE__, "bad object ID");
+
+      sec->addr = SPUADDR (fd, sec->addr);
+      sec->endaddr = SPUADDR (fd, sec->endaddr);
+    }
+}
+
+
+/* Inferior memory should contain an SPE executable image at location ADDR.
+   Allocate a BFD representing that executable.  Return NULL on error.  */
+
+static void *
+spu_bfd_iovec_open (bfd *nbfd, void *open_closure)
+{
+  return open_closure;
+}
+
+static int
+spu_bfd_iovec_close (bfd *nbfd, void *stream)
+{
+  xfree (stream);
+  return 1;
+}
+
+static file_ptr
+spu_bfd_iovec_pread (bfd *abfd, void *stream, void *buf,
+                     file_ptr nbytes, file_ptr offset)
+{
+  CORE_ADDR addr = *(CORE_ADDR *)stream;
+  int ret;
+
+  /* Switch to the PPE architecture while reading target memory.
+     This should not be necessary, but it is right now.  */
+  struct cleanup *old_chain = save_current_gdbarch ();
+  current_gdbarch = target_gdbarch;
+  ret = target_read_memory (addr + offset, buf, nbytes);
+  do_cleanups (old_chain);
+
+  if (ret != 0)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return -1;
+    }
+
+  return nbytes;
+}
+
+static int
+spu_bfd_iovec_stat (bfd *abfd, void *stream, struct stat *sb)
+{
+  /* We don't have an easy way of finding the size of embedded spu
+     images.  We could parse the in-memory ELF header and section
+     table to find the extent of the last section but that seems
+     pointless when the size is needed only for checks of other
+     parsed values in dbxread.c.  */
+  sb->st_size = INT_MAX;
+  return 0;
+}
+
+static bfd *
+spu_bfd_open (char *name, CORE_ADDR addr)
+{
+  bfd *nbfd;
+
+  CORE_ADDR *open_closure = xmalloc (sizeof (CORE_ADDR));
+  *open_closure = addr;
+
+  nbfd = bfd_openr_iovec (xstrdup (name), "elf32-spu",
+                          spu_bfd_iovec_open, open_closure,
+                          spu_bfd_iovec_pread, spu_bfd_iovec_close,
+			  spu_bfd_iovec_stat);
+  if (!nbfd)
+    return NULL;
+
+  if (!bfd_check_format (nbfd, bfd_object))
+    {
+      bfd_close (nbfd);
+      return NULL;
+    }
+
+  return nbfd;
+}
+
+/* Open shared library BFD.  */
+static bfd *
+spu_open_bfd (char *pathname)
+{
+  char *original_name = strrchr (pathname, '@');
+  char name[64];
+  bfd *abfd;
+  asection *spu_name;
+  unsigned long long addr;
+  int fd;
+
+  if (!original_name)
+    return NULL;
+
+  /* Decode object ID.  */
+  if (sscanf (original_name, "@0x%llx <%d>", &addr, &fd) != 2)
+    internal_error (__FILE__, __LINE__, "bad object ID");
+
+  /* Open BFD representing SPE executable.  */
+  abfd = spu_bfd_open (original_name, (CORE_ADDR) addr);
+  if (!abfd)
+    error (_("Cannot read SPE executable at %s"), original_name);
+
+  /* Retrieve SPU name note.  */
+  spu_name = bfd_get_section_by_name (abfd, ".note.spu_name");
+  if (spu_name)
+    {
+      int sect_size = bfd_section_size (abfd, spu_name);
+      if (sect_size > 20)
+	{
+	  char *buf = alloca (sect_size - 20 + strlen (original_name) + 1);
+	  bfd_get_section_contents (abfd, spu_name, buf, 20, sect_size - 20);
+	  buf[sect_size - 20] = '\0';
+
+	  strcat (buf, original_name);
+
+	  xfree ((char *)abfd->filename);
+	  abfd->filename = xstrdup (buf);
+	}
+    }
+
+  return abfd;
+}
+
+/* Lookup global symbol in a SPE executable.  */
+static struct symbol *
+spu_lookup_lib_symbol (const struct objfile *objfile,
+		       const char *name,
+		       const char *linkage_name,
+		       const domain_enum domain)
+{
+  if (bfd_get_arch (objfile->obfd) == bfd_arch_spu)
+    return lookup_global_symbol_from_objfile (objfile, name, linkage_name,
+					      domain);
+
+  if (svr4_so_ops.lookup_lib_global_symbol != NULL)
+    return svr4_so_ops.lookup_lib_global_symbol (objfile, name, linkage_name,
+						 domain);
+  return NULL;
+}
+
+/* Enable shared library breakpoint.  */
+static int
+spu_enable_break (struct objfile *objfile)
+{
+  struct minimal_symbol *spe_event_sym = NULL;
+
+  /* The libspe library will call __spe_context_update_event whenever any
+     SPE context is allocated or destroyed.  */
+  spe_event_sym = lookup_minimal_symbol ("__spe_context_update_event",
+					 NULL, objfile);
+
+  /* Place a solib_event breakpoint on the symbol.  */
+  if (spe_event_sym)
+    {
+      CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (spe_event_sym);
+      addr = gdbarch_convert_from_func_ptr_addr (target_gdbarch, addr,
+                                                 &current_target);
+      create_solib_event_breakpoint (addr);
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Create inferior hook.  */
+static void
+spu_solib_create_inferior_hook (void)
+{
+  /* Remove all previously installed solib breakpoints.  Both the SVR4
+     code and us will re-install all required breakpoints.  */
+  remove_solib_event_breakpoints ();
+
+  /* Handle SPE stand-alone executables.  */
+  if (spu_standalone_p ())
+    {
+      /* After an SPE stand-alone executable was loaded, we'll receive
+	 an additional trap due to the binfmt_misc handler.  Make sure
+	 to skip that trap.  */
+      spu_skip_standalone_loader ();
+
+      /* At this point, the target is executing, so GDB expects to be
+	 able to read/write target memory.  For the stand-alone case,
+	 "target memory" is actually the local store of the stand-alone
+	 SPE context -- but at this point this context has not yet been
+	 allocated.  This is a problem in particular for breakpoints that
+	 may already have been established.
+
+	 We solve this by installing the multi-architecture handler
+	 already at this point.  This target will understand that
+	 breakpoints falling the range of addresses covered by the
+	 SPE local store need to be ignored.
+
+	 (Note that this needs to be done here, before the solib code gets
+	 active -- otherwise we'd fail on the first breakpoint_re_set call
+	 triggered when new shared libraries are found.)
+
+	 Once the actual stand-alone SPE context is installed, spu_current_sos
+	 will detect this and relocate the main executable to its final (valid)
+	 address range.  Breakpoints will be automatically re-inserted at
+	 that point.  */
+      spu_multiarch_enable ();
+
+      /* A special case arises when re-starting an executable, because at
+	 this point it still resides at the relocated address range that was
+	 determined during its last execution.  We need to undo the relocation
+	 so that that multi-architecture target recognizes the stand-alone
+	 initialization special case.  */
+      spu_relocate_main_executable (-1);
+    }
+
+  /* Call SVR4 hook -- this will re-insert the SVR4 solib breakpoints.  */
+  svr4_so_ops.solib_create_inferior_hook ();
+
+  /* If the inferior is statically linked against libspe, we need to install
+     our own solib breakpoint right now.  Otherwise, it will be installed by
+     the solib_loaded observer below as soon as libspe is loaded.  */
+  spu_enable_break (NULL);
+}
+
+/* Install SPE "shared library" handling.  This is called by -tdep code
+   that wants to support SPU as a secondary architecture.  */
+void
+set_spu_solib_ops (struct gdbarch *gdbarch)
+{
+  static struct target_so_ops spu_so_ops;
+
+  /* Initialize this lazily, to avoid an initialization order
+     dependency on solib-svr4.c's _initialize routine.  */
+  if (spu_so_ops.current_sos == NULL)
+    {
+      spu_so_ops = svr4_so_ops;
+      spu_so_ops.solib_create_inferior_hook = spu_solib_create_inferior_hook;
+      spu_so_ops.relocate_section_addresses = spu_relocate_section_addresses;
+      spu_so_ops.free_so = spu_free_so;
+      spu_so_ops.current_sos = spu_current_sos;
+      spu_so_ops.open_bfd = spu_open_bfd;
+      spu_so_ops.lookup_lib_global_symbol = spu_lookup_lib_symbol;
+    }
+
+  set_solib_ops (gdbarch, &spu_so_ops);
+}
+
+/* Observer for the solib_loaded event.  Used to install our breakpoint
+   if libspe is a shared library.  */
+static void
+spu_solib_loaded (struct so_list *so)
+{
+  if (strstr (so->so_original_name, "/libspe") != NULL)
+    {
+      solib_read_symbols (so, so->from_tty);
+      spu_enable_break (so->objfile);
+    }
+}
+
+void
+_initialize_spu_solib (void)
+{
+  observer_attach_solib_loaded (spu_solib_loaded);
+}
+
Index: src/gdb/spu-multiarch.c
===================================================================
--- /dev/null
+++ src/gdb/spu-multiarch.c
@@ -0,0 +1,450 @@
+/* Cell SPU GNU/Linux multi-architecture debugging support.
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdbcmd.h"
+#include "gdb_string.h"
+#include "gdb_assert.h"
+#include "arch-utils.h"
+#include "observer.h"
+#include "inferior.h"
+#include "regcache.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "block.h"
+#include "exec.h"
+#include "target-descriptions.h"
+
+#include "ppc-tdep.h"
+#include "ppc-linux-tdep.h"
+#include "spu-tdep.h"
+
+/* This module's target vector.  */
+static struct target_ops spu_ops;
+
+/* Stand-alone SPE executable?  */
+#define spu_standalone_p() \
+  (symfile_objfile && symfile_objfile->obfd \
+   && bfd_get_arch (symfile_objfile->obfd) == bfd_arch_spu)
+
+/* PPU side system calls.  */
+#define INSTR_SC	0x44000002
+#define NR_spu_run	0x0116
+
+/* If the PPU thread is currently stopped on a spu_run system call,
+   return to FD and ADDR the file handle and NPC parameter address
+   used with the system call.  Return non-zero if successful.  */
+static int
+parse_spufs_run (ptid_t ptid, int *fd, CORE_ADDR *addr)
+{
+  struct gdbarch_tdep *tdep;
+  struct regcache *regcache;
+  char buf[4];
+  CORE_ADDR pc;
+  ULONGEST regval;
+
+  /* If we're not on PPU, there's nothing to detect.  */
+  if (gdbarch_bfd_arch_info (target_gdbarch)->arch != bfd_arch_powerpc)
+    return 0;
+
+  /* Get PPU-side registers.  */
+  regcache = get_thread_arch_regcache (ptid, target_gdbarch);
+  tdep = gdbarch_tdep (target_gdbarch);
+
+  /* Fetch instruction preceding current NIP.  */
+  if (target_read_memory (regcache_read_pc (regcache) - 4, buf, 4) != 0)
+    return 0;
+  /* It should be a "sc" instruction.  */
+  if (extract_unsigned_integer (buf, 4) != INSTR_SC)
+    return 0;
+  /* System call number should be NR_spu_run.  */
+  regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum, &regval);
+  if (regval != NR_spu_run)
+    return 0;
+
+  /* Register 3 contains fd, register 4 the NPC param pointer.  */
+  regcache_cooked_read_unsigned (regcache, PPC_ORIG_R3_REGNUM, &regval);
+  *fd = (int) regval;
+  regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 4, &regval);
+  *addr = (CORE_ADDR) regval;
+  return 1;
+}
+
+/* Find gdbarch for SPU context SPUFS_FD.  */
+static struct gdbarch *
+spu_gdbarch (int spufs_fd)
+{
+  struct gdbarch_info info;
+  gdbarch_info_init (&info);
+  info.bfd_arch_info = bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu);
+  info.byte_order = BFD_ENDIAN_BIG;
+  info.osabi = GDB_OSABI_LINUX;
+  info.tdep_info = (void *) &spufs_fd;
+  return gdbarch_find_by_info (info);
+}
+
+/* Override the to_thread_architecture routine.  */
+static struct gdbarch *
+spu_thread_architecture (struct target_ops *ops, ptid_t ptid)
+{
+  int spufs_fd;
+  ULONGEST spufs_addr;
+
+  if (parse_spufs_run (ptid, &spufs_fd, &spufs_addr))
+    return spu_gdbarch (spufs_fd);
+
+  return target_gdbarch;
+}
+
+/* Override the to_wait routine to detect current architecture.  */
+static ptid_t
+spu_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
+
+  /* Always switch to PPU while running the inferior.  This allows
+     linux-thread-db.c code to work as expected.  */
+  current_gdbarch = target_gdbarch;
+
+  /* Run it.  */
+  ptid = ops_beneath->to_wait (ptid, ourstatus);
+
+  /* Detect and switch to current architecture.  */
+  if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
+    current_gdbarch = spu_thread_architecture (&current_target, ptid);
+
+  return ptid;
+}
+
+
+/* Override the to_region_ok_for_hw_watchpoint routine.  */
+static int
+spu_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+{
+  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
+
+  while (ops_beneath && !ops_beneath->to_region_ok_for_hw_watchpoint)
+    ops_beneath = find_target_beneath (ops_beneath);
+  gdb_assert (ops_beneath);
+
+  /* Ignore attempts to remove breakpoints in a stand-alone SPE
+     executable just after startup, before we know the FD.  */
+  if (spu_standalone_p () && addr < SPU_LS_SIZE)
+    return -1;
+
+  /* We cannot watch SPU local store.  */
+  if (SPUADDR_SPU (addr) != -1)
+    return 0;
+
+  if (ops_beneath)
+    return ops_beneath->to_region_ok_for_hw_watchpoint (addr, len);
+
+  return 0;
+}
+
+/* Override the to_insert_breakpoint routine.  */
+static int
+spu_insert_breakpoint (struct bp_target_info *bp)
+{
+  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
+  CORE_ADDR addr = bp->placed_address;
+  struct cleanup *old_chain;
+  int ret;
+
+  while (ops_beneath && !ops_beneath->to_insert_breakpoint)
+    ops_beneath = find_target_beneath (ops_beneath);
+  gdb_assert (ops_beneath);
+
+  /* Ignore attempts to insert breakpoints in a stand-alone SPE
+     executable just after startup, before we know the FD.  */
+  if (spu_standalone_p () && addr < SPU_LS_SIZE)
+    return -1;
+
+  old_chain = save_current_gdbarch ();
+  if (SPUADDR_SPU (addr) != -1)
+    current_gdbarch = spu_gdbarch (SPUADDR_SPU (addr));
+  else
+    current_gdbarch = target_gdbarch;
+  ret = ops_beneath->to_insert_breakpoint (bp);
+  do_cleanups (old_chain);
+
+  return ret;
+}
+
+/* Override the to_remove_breakpoint routine.  */
+static int
+spu_remove_breakpoint (struct bp_target_info *bp)
+{
+  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
+  CORE_ADDR addr = bp->placed_address;
+  struct cleanup *old_chain;
+  int ret;
+
+  while (ops_beneath && !ops_beneath->to_remove_breakpoint)
+    ops_beneath = find_target_beneath (ops_beneath);
+  gdb_assert (ops_beneath);
+
+  /* Ignore attempts to remove breakpoints in a stand-alone SPE
+     executable just after startup, before we know the FD.  */
+  if (spu_standalone_p () && addr < SPU_LS_SIZE)
+    return -1;
+
+  old_chain = save_current_gdbarch ();
+  if (SPUADDR_SPU (addr) != -1)
+    current_gdbarch = spu_gdbarch (SPUADDR_SPU (addr));
+  else
+    current_gdbarch = target_gdbarch;
+  ret = ops_beneath->to_remove_breakpoint (bp);
+  do_cleanups (old_chain);
+
+  return ret;
+}
+
+
+/* Override the to_fetch_registers routine.  */
+static void
+spu_fetch_registers (struct regcache *regcache, int regno)
+{
+  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
+  int spufs_fd;
+  ULONGEST spufs_addr;
+
+  /* This version applies only if we're currently in spu_run.  */
+  if (gdbarch_bfd_arch_info (get_regcache_arch (regcache))->arch
+      != bfd_arch_spu)
+    {
+      while (ops_beneath && !ops_beneath->to_fetch_registers)
+	ops_beneath = find_target_beneath (ops_beneath);
+
+      gdb_assert (ops_beneath);
+      ops_beneath->to_fetch_registers (regcache, regno);
+      return;
+    }
+
+  /* We must be stopped on a spu_run system call.  */
+  if (!parse_spufs_run (inferior_ptid, &spufs_fd, &spufs_addr))
+    return;
+
+  /* The ID register holds the spufs file handle.  */
+  if (regno == -1 || regno == SPU_ID_REGNUM)
+    {
+      char buf[4];
+      store_unsigned_integer (buf, 4, spufs_fd);
+      regcache_raw_supply (regcache, SPU_ID_REGNUM, buf);
+    }
+
+  /* The NPC register is found in PPC memory at SPUFS_ADDR.  */
+  if (regno == -1 || regno == SPU_PC_REGNUM)
+    {
+      struct cleanup *old_chain;
+      LONGEST ret;
+      char buf[4];
+
+      /* Switch to PPC arch while accessing target memory.  */
+      old_chain = save_current_gdbarch ();
+      current_gdbarch = target_gdbarch;
+      ret = target_read (ops_beneath, TARGET_OBJECT_MEMORY, NULL,
+			 buf, spufs_addr, sizeof buf);
+      do_cleanups (old_chain);
+
+      if (ret == sizeof buf)
+	regcache_raw_supply (regcache, SPU_PC_REGNUM, buf);
+    }
+
+  /* The GPRs are found in the "regs" spufs file.  */
+  if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+    {
+      char buf[16 * SPU_NUM_GPRS], annex[32];
+      int i;
+
+      xsnprintf (annex, sizeof annex, "%d/regs", spufs_fd);
+      if (target_read (ops_beneath, TARGET_OBJECT_SPU, annex,
+		       buf, 0, sizeof buf) == sizeof buf)
+	for (i = 0; i < SPU_NUM_GPRS; i++)
+	  regcache_raw_supply (regcache, i, buf + i*16);
+    }
+}
+
+/* Override the to_store_registers routine.  */
+static void
+spu_store_registers (struct regcache *regcache, int regno)
+{
+  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
+  int spufs_fd;
+  ULONGEST spufs_addr;
+
+  /* This version applies only if we're currently in spu_run.  */
+  if (gdbarch_bfd_arch_info (get_regcache_arch (regcache))->arch
+      != bfd_arch_spu)
+    {
+      while (ops_beneath && !ops_beneath->to_fetch_registers)
+	ops_beneath = find_target_beneath (ops_beneath);
+
+      gdb_assert (ops_beneath);
+      ops_beneath->to_store_registers (regcache, regno);
+      return;
+    }
+
+  /* We must be stopped on a spu_run system call.  */
+  if (!parse_spufs_run (inferior_ptid, &spufs_fd, &spufs_addr))
+    return;
+
+  /* The NPC register is found in PPC memory at SPUFS_ADDR.  */
+  if (regno == -1 || regno == SPU_PC_REGNUM)
+    {
+      struct cleanup *old_chain;
+      char buf[4];
+      regcache_raw_collect (regcache, SPU_PC_REGNUM, buf);
+
+      /* Switch to PPC arch while accessing target memory.  */
+      old_chain = save_current_gdbarch ();
+      current_gdbarch = target_gdbarch;
+      target_write (ops_beneath, TARGET_OBJECT_MEMORY, NULL,
+		    buf, spufs_addr, sizeof buf);
+      do_cleanups (old_chain);
+    }
+
+  /* The GPRs are found in the "regs" spufs file.  */
+  if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+    {
+      char buf[16 * SPU_NUM_GPRS], annex[32];
+      int i;
+
+      for (i = 0; i < SPU_NUM_GPRS; i++)
+	regcache_raw_collect (regcache, i, buf + i*16);
+
+      xsnprintf (annex, sizeof annex, "%d/regs", spufs_fd);
+      target_write (ops_beneath, TARGET_OBJECT_SPU, annex,
+		    buf, 0, sizeof buf);
+    }
+}
+
+/* Override the to_xfer_partial routine.  */
+static LONGEST
+spu_xfer_partial (struct target_ops *ops, enum target_object object,
+		  const char *annex, gdb_byte *readbuf,
+		  const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+  struct target_ops *ops_beneath = find_target_beneath (ops);
+
+  /* Use the "mem" spufs file to access SPU local store.  */
+  if (object == TARGET_OBJECT_MEMORY)
+    {
+      int fd = SPUADDR_SPU (offset);
+      CORE_ADDR addr = SPUADDR_ADDR (offset);
+      char mem_annex[32];
+
+      if (fd >= 0 && addr < SPU_LS_SIZE)
+	{
+	  xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd);
+	  return ops_beneath->to_xfer_partial (ops_beneath, TARGET_OBJECT_SPU,
+					       mem_annex, readbuf, writebuf,
+					       addr, len);
+	}
+    }
+
+  return ops_beneath->to_xfer_partial (ops_beneath, object, annex,
+				       readbuf, writebuf, offset, len);
+}
+
+/* Override the to_search_memory routine.  */
+static int
+spu_search_memory (struct target_ops* ops,
+		   CORE_ADDR start_addr, ULONGEST search_space_len,
+		   const gdb_byte *pattern, ULONGEST pattern_len,
+		   CORE_ADDR *found_addrp)
+{
+  struct target_ops *ops_beneath = find_target_beneath (ops);
+  while (ops_beneath && !ops_beneath->to_search_memory)
+    ops_beneath = find_target_beneath (ops_beneath);
+
+  /* For SPU local store, always fall back to the simple method.  Likewise
+     if we do not have any target-specific special implementation.  */
+  if (!ops_beneath || SPUADDR_SPU (start_addr) >= 0)
+    return simple_search_memory (ops,
+				 start_addr, search_space_len,
+				 pattern, pattern_len, found_addrp);
+
+  return ops_beneath->to_search_memory (ops_beneath,
+					start_addr, search_space_len,
+					pattern, pattern_len, found_addrp);
+}
+
+
+/* Push and pop the SPU multi-architecture support target.  */
+
+void
+spu_multiarch_enable (void)
+{
+  /* If GDB was configured without SPU architecture support,
+     we cannot install SPU multi-architecture support either.  */
+  if (spu_gdbarch (-1) == NULL)
+    return;
+
+  push_target (&spu_ops);
+}
+
+void
+spu_multiarch_disable (void)
+{
+  unpush_target (&spu_ops);
+}
+
+static void
+spu_mourn_inferior (void)
+{
+  find_target_beneath (&spu_ops)->to_mourn_inferior ();
+  spu_multiarch_disable ();
+}
+
+
+/* Initialize the SPU multi-architecture support target.  */
+
+static void
+init_spu_ops (void)
+{
+  spu_ops.to_shortname = "spu";
+  spu_ops.to_longname = "SPU multi-architecture support.";
+  spu_ops.to_doc = "SPU multi-architecture support.";
+  spu_ops.to_mourn_inferior = spu_mourn_inferior;
+  spu_ops.to_fetch_registers = spu_fetch_registers;
+  spu_ops.to_store_registers = spu_store_registers;
+  spu_ops.to_xfer_partial = spu_xfer_partial;
+  spu_ops.to_search_memory = spu_search_memory;
+  spu_ops.to_insert_breakpoint = spu_insert_breakpoint;
+  spu_ops.to_remove_breakpoint = spu_remove_breakpoint;
+  spu_ops.to_region_ok_for_hw_watchpoint = spu_region_ok_for_hw_watchpoint;
+  spu_ops.to_wait = spu_wait;
+  spu_ops.to_thread_architecture = spu_thread_architecture;
+  spu_ops.to_stratum = arch_stratum;
+  spu_ops.to_magic = OPS_MAGIC;
+}
+
+void
+_initialize_spu_multiarch (void)
+{
+  /* Install ourselves on the target stack.  */
+  init_spu_ops ();
+  add_target (&spu_ops);
+}
+
Index: src/gdb/spu-tdep.c
===================================================================
--- src.orig/gdb/spu-tdep.c
+++ src/gdb/spu-tdep.c
@@ -48,6 +48,11 @@
 /* The tdep structure.  */
 struct gdbarch_tdep
 {
+  /* The spufs ID identifying our address space.  */
+  int id;
+  /* The size of this address space.  */
+  CORE_ADDR lslr;
+
   /* SPU-specific vector type.  */
   struct type *spu_builtin_type_vec128;
 };
@@ -325,31 +330,44 @@ spu_register_reggroup_p (struct gdbarch 
 /* Address conversion.  */
 
 static CORE_ADDR
+spu_lslr (int id)
+{
+  gdb_byte buf[16];
+  char annex[32];
+
+  xsnprintf (annex, sizeof annex, "%d/lslr", id);
+  memset (buf, 0, sizeof buf);
+  target_read (&current_target, TARGET_OBJECT_SPU, annex,
+	       buf, 0, sizeof buf);
+
+  return strtoulst (buf, NULL, 16);
+}
+
+static void
+spu_address_to_pointer (struct gdbarch *gdbarch,
+			struct type *type, gdb_byte *buf, CORE_ADDR addr)
+{
+  store_unsigned_integer (buf, TYPE_LENGTH (type), SPUADDR_ADDR (addr));
+}
+
+static CORE_ADDR
 spu_pointer_to_address (struct gdbarch *gdbarch,
 			struct type *type, const gdb_byte *buf)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   ULONGEST addr = extract_unsigned_integer (buf, TYPE_LENGTH (type));
-  ULONGEST lslr = SPU_LS_SIZE - 1; /* Hard-wired LS size.  */
-
-  if (target_has_registers && target_has_stack && target_has_memory)
-    lslr = get_frame_register_unsigned (get_selected_frame (NULL),
-					SPU_LSLR_REGNUM);
 
-  return addr & lslr;
+  return addr? SPUADDR (tdep->id, addr & tdep->lslr) : 0;
 }
 
 static CORE_ADDR
 spu_integer_to_address (struct gdbarch *gdbarch,
 			struct type *type, const gdb_byte *buf)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   ULONGEST addr = unpack_long (type, buf);
-  ULONGEST lslr = SPU_LS_SIZE - 1; /* Hard-wired LS size.  */
-
-  if (target_has_registers && target_has_stack && target_has_memory)
-    lslr = get_frame_register_unsigned (get_selected_frame (NULL),
-					SPU_LSLR_REGNUM);
 
-  return addr & lslr;
+  return SPUADDR (tdep->id, addr & tdep->lslr);
 }
 
 
@@ -839,8 +857,11 @@ static struct spu_unwind_cache *
 spu_frame_unwind_cache (struct frame_info *this_frame,
 			void **this_prologue_cache)
 {
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   struct spu_unwind_cache *info;
   struct spu_prologue_data data;
+  CORE_ADDR id = tdep->id;
   gdb_byte buf[16];
 
   if (*this_prologue_cache)
@@ -873,6 +894,7 @@ spu_frame_unwind_cache (struct frame_inf
       /* Determine CFA via unwound CFA_REG plus CFA_OFFSET.  */
       get_frame_register (this_frame, data.cfa_reg, buf);
       cfa = extract_unsigned_integer (buf, 4) + data.cfa_offset;
+      cfa = SPUADDR (id, cfa);
 
       /* Call-saved register slots.  */
       for (i = 0; i < SPU_NUM_GPRS; i++)
@@ -895,7 +917,7 @@ spu_frame_unwind_cache (struct frame_inf
 
       /* Get the backchain.  */
       reg = get_frame_register_unsigned (this_frame, SPU_SP_REGNUM);
-      status = safe_read_memory_integer (reg, 4, &backchain);
+      status = safe_read_memory_integer (SPUADDR (id, reg), 4, &backchain);
 
       /* A zero backchain terminates the frame chain.  Also, sanity
          check against the local store size limit.  */
@@ -903,11 +925,11 @@ spu_frame_unwind_cache (struct frame_inf
 	{
 	  /* Assume the link register is saved into its slot.  */
 	  if (backchain + 16 < SPU_LS_SIZE)
-	    info->saved_regs[SPU_LR_REGNUM].addr = backchain + 16;
+	    info->saved_regs[SPU_LR_REGNUM].addr = SPUADDR (id, backchain + 16);
 
           /* Frame bases.  */
-	  info->frame_base = backchain;
-	  info->local_base = reg;
+	  info->frame_base = SPUADDR (id, backchain);
+	  info->local_base = SPUADDR (id, reg);
 	}
     }
 
@@ -916,7 +938,8 @@ spu_frame_unwind_cache (struct frame_inf
     return info;
 
   /* The previous SP is equal to the CFA.  */
-  trad_frame_set_value (info->saved_regs, SPU_SP_REGNUM, info->frame_base);
+  trad_frame_set_value (info->saved_regs, SPU_SP_REGNUM,
+			SPUADDR_ADDR (info->frame_base));
 
   /* Read full contents of the unwound link register in order to
      be able to determine the return address.  */
@@ -994,24 +1017,28 @@ static const struct frame_base spu_frame
 static CORE_ADDR
 spu_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   CORE_ADDR pc = frame_unwind_register_unsigned (next_frame, SPU_PC_REGNUM);
   /* Mask off interrupt enable bit.  */
-  return pc & -4;
+  return SPUADDR (tdep->id, pc & -4);
 }
 
 static CORE_ADDR
 spu_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
 {
-  return frame_unwind_register_unsigned (next_frame, SPU_SP_REGNUM);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  CORE_ADDR sp = frame_unwind_register_unsigned (next_frame, SPU_SP_REGNUM);
+  return SPUADDR (tdep->id, sp);
 }
 
 static CORE_ADDR
 spu_read_pc (struct regcache *regcache)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (get_regcache_arch (regcache));
   ULONGEST pc;
   regcache_cooked_read_unsigned (regcache, SPU_PC_REGNUM, &pc);
   /* Mask off interrupt enable bit.  */
-  return pc & -4;
+  return SPUADDR (tdep->id, pc & -4);
 }
 
 static void
@@ -1021,7 +1048,7 @@ spu_write_pc (struct regcache *regcache,
   ULONGEST old_pc;
   regcache_cooked_read_unsigned (regcache, SPU_PC_REGNUM, &old_pc);
   regcache_cooked_write_unsigned (regcache, SPU_PC_REGNUM,
-				  (pc & -4) | (old_pc & 3));
+				  (SPUADDR_ADDR (pc) & -4) | (old_pc & 3));
 }
 
 
@@ -1132,7 +1159,7 @@ spu_push_dummy_call (struct gdbarch *gdb
 
   /* Set the return address.  */
   memset (buf, 0, sizeof buf);
-  store_unsigned_integer (buf, 4, bp_addr);
+  store_unsigned_integer (buf, 4, SPUADDR_ADDR (bp_addr));
   regcache_cooked_write (regcache, SPU_LR_REGNUM, buf);
 
   /* If STRUCT_RETURN is true, then the struct return address (in
@@ -1141,7 +1168,7 @@ spu_push_dummy_call (struct gdbarch *gdb
   if (struct_return)
     {
       memset (buf, 0, sizeof buf);
-      store_unsigned_integer (buf, 4, struct_addr);
+      store_unsigned_integer (buf, 4, SPUADDR_ADDR (struct_addr));
       regcache_cooked_write (regcache, regnum++, buf);
     }
 
@@ -1219,9 +1246,10 @@ spu_push_dummy_call (struct gdbarch *gdb
 static struct frame_id
 spu_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   CORE_ADDR pc = get_frame_register_unsigned (this_frame, SPU_PC_REGNUM);
   CORE_ADDR sp = get_frame_register_unsigned (this_frame, SPU_SP_REGNUM);
-  return frame_id_build (sp, pc & -4);
+  return frame_id_build (SPUADDR (tdep->id, sp), SPUADDR (tdep->id, pc & -4));
 }
 
 /* Function return value access.  */
@@ -1301,18 +1329,18 @@ spu_software_single_step (struct frame_i
      instruction is a PPE-assisted call, in which case it is at PC + 8.
      Wrap around LS limit to be on the safe side.  */
   if ((insn & 0xffffff00) == 0x00002100)
-    next_pc = (pc + 8) & (SPU_LS_SIZE - 1);
+    next_pc = (SPUADDR_ADDR (pc) + 8) & (SPU_LS_SIZE - 1);
   else
-    next_pc = (pc + 4) & (SPU_LS_SIZE - 1);
+    next_pc = (SPUADDR_ADDR (pc) + 4) & (SPU_LS_SIZE - 1);
 
-  insert_single_step_breakpoint (next_pc);
+  insert_single_step_breakpoint (SPUADDR (SPUADDR_SPU (pc), next_pc));
 
   if (is_branch (insn, &offset, &reg))
     {
       CORE_ADDR target = offset;
 
       if (reg == SPU_PC_REGNUM)
-	target += pc;
+	target += SPUADDR_ADDR (pc);
       else if (reg != -1)
 	{
 	  get_frame_register_bytes (frame, reg, 0, 4, buf);
@@ -1321,12 +1349,35 @@ spu_software_single_step (struct frame_i
 
       target = target & (SPU_LS_SIZE - 1);
       if (target != next_pc)
-	insert_single_step_breakpoint (target);
+	insert_single_step_breakpoint (SPUADDR (SPUADDR_SPU (pc), target));
     }
 
   return 1;
 }
 
+
+/* Disassembler.  */
+
+static void
+spu_dis_asm_print_address (bfd_vma addr, struct disassemble_info *info)
+{
+  int id = *(int *)info->application_data;
+  CORE_ADDR spu_addr = SPUADDR (id, addr);
+  print_address (spu_addr, info->stream);
+}
+
+static int
+gdb_print_insn_spu (bfd_vma memaddr, disassemble_info * info)
+{
+  /* The opcodes disassembler does 18-bit address arithmetic.  Make sure the
+     SPU ID encoded in the high bits is added back when we call print_address.  */
+  int id = SPUADDR_SPU (memaddr);
+  info->application_data = &id;
+  info->print_address_func = spu_dis_asm_print_address;
+  return print_insn_spu (memaddr, info);
+}
+
+
 /* Target overlays for the SPU overlay manager.
 
    See the documentation of simple_overlay_update for how the
@@ -1443,7 +1494,7 @@ static void
 spu_overlay_update_osect (struct obj_section *osect)
 {
   struct spu_overlay_table *ovly_table;
-  CORE_ADDR val;
+  CORE_ADDR id, val;
 
   ovly_table = spu_get_overlay_table (osect->objfile);
   if (!ovly_table)
@@ -1453,7 +1504,8 @@ spu_overlay_update_osect (struct obj_sec
   if (ovly_table->mapped_ptr == 0)
     return;
 
-  val = read_memory_unsigned_integer (ovly_table->mapped_ptr, 4);
+  id = SPUADDR_SPU (obj_section_addr (osect));
+  val = read_memory_unsigned_integer (SPUADDR (id, ovly_table->mapped_ptr), 4);
   osect->ovly_mapped = (val == ovly_table->mapped_val);
 }
 
@@ -2071,22 +2123,30 @@ spu_gdbarch_init (struct gdbarch_info in
 {
   struct gdbarch *gdbarch;
   struct gdbarch_tdep *tdep;
+  int id = -1;
 
-  /* Find a candidate among the list of pre-declared architectures.  */
-  arches = gdbarch_list_lookup_by_info (arches, &info);
-  if (arches != NULL)
-    return arches->gdbarch;
+  /* Which spufs ID was requested as address space?  */
+  if (info.tdep_info)
+    id = *(int *)info.tdep_info;
 
-  /* Is is for us?  */
-  if (info.bfd_arch_info->mach != bfd_mach_spu)
-    return NULL;
+  /* Find a candidate among extant architectures.  */
+  for (arches = gdbarch_list_lookup_by_info (arches, &info);
+       arches != NULL;
+       arches = gdbarch_list_lookup_by_info (arches->next, &info))
+    {
+      tdep = gdbarch_tdep (arches->gdbarch);
+      if (tdep && tdep->id == id)
+	return arches->gdbarch;
+    }
 
-  /* Yes, create a new architecture.  */
+  /* None found, so create a new architecture.  */
   tdep = XCALLOC (1, struct gdbarch_tdep);
+  tdep->id = id;
+  tdep->lslr = id != -1? spu_lslr (id) : SPU_LS_SIZE - 1;
   gdbarch = gdbarch_alloc (&info, tdep);
 
   /* Disassembler.  */
-  set_gdbarch_print_insn (gdbarch, print_insn_spu);
+  set_gdbarch_print_insn (gdbarch, gdb_print_insn_spu);
 
   /* Registers.  */
   set_gdbarch_num_regs (gdbarch, SPU_NUM_REGS);
@@ -2118,6 +2178,7 @@ spu_gdbarch_init (struct gdbarch_info in
   set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
 
   /* Address conversion.  */
+  set_gdbarch_address_to_pointer (gdbarch, spu_address_to_pointer);
   set_gdbarch_pointer_to_address (gdbarch, spu_pointer_to_address);
   set_gdbarch_integer_to_address (gdbarch, spu_integer_to_address);
 
Index: src/gdb/spu-tdep.h
===================================================================
--- src.orig/gdb/spu-tdep.h
+++ src/gdb/spu-tdep.h
@@ -50,4 +50,17 @@ enum spu_regnum
 /* Local store.  */
 #define SPU_LS_SIZE          0x40000
 
+/* Address conversions.  */
+#define SPUADDR(spu, addr) \
+  ((spu) != -1? (ULONGEST)1 << 63 | (ULONGEST)(spu) << 32 | (addr) : (addr))
+#define SPUADDR_SPU(addr) \
+  (((addr) & (ULONGEST)1 << 63)? (ULONGEST)(addr) >> 32 & 0x7fffffff : -1)
+#define SPUADDR_ADDR(addr) \
+  (((addr) & (ULONGEST)1 << 63)? (ULONGEST)(addr) & 0xffffffff : (addr))
+
+/* SPU multi-architecture support.  */
+extern void set_spu_solib_ops (struct gdbarch *gdbarch);
+extern void spu_multiarch_enable (void);
+extern void spu_multiarch_disable (void);
+
 #endif
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c
+++ src/gdb/target.c
@@ -2031,7 +2031,8 @@ target_require_runnable (void)
       /* Do not worry about thread_stratum targets that can not
 	 create inferiors.  Assume they will be pushed again if
 	 necessary, and continue to the process_stratum.  */
-      if (t->to_stratum == thread_stratum)
+      if (t->to_stratum == thread_stratum
+	  || t->to_stratum == arch_stratum)
 	continue;
 
       error (_("\
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h
+++ src/gdb/target.h
@@ -62,7 +62,8 @@ enum strata
     file_stratum,		/* Executable files, etc */
     core_stratum,		/* Core dump files */
     process_stratum,		/* Executing processes */
-    thread_stratum		/* Executing threads */
+    thread_stratum,		/* Executing threads */
+    arch_stratum		/* Architecture overrides */
   };
 
 enum thread_control_capabilities
Index: src/gdb/testsuite/gdb.xml/tdesc-regs.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.xml/tdesc-regs.exp
+++ src/gdb/testsuite/gdb.xml/tdesc-regs.exp
@@ -39,6 +39,16 @@ switch -glob -- [istarget] {
 	set regdir "rs6000/"
 	set core-regs {power-core.xml}
     }
+    "spu*-*-*" {
+	# This may be either the spu-linux-nat target, or the Cell/B.E.
+	# multi-architecture debugger in SPU standalone executable mode.
+	# We do not support XML register sets on SPU in either case.
+	# However, the multi-arch debugger will accept XML registers sets
+	# (on the PowerPC side), hence the test below would fail.
+	# Simply return unconditionally here.
+	unsupported "register tests"
+	return 0
+    }
 }
 
 # If no core registers were specified, assume this target does not
-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com


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