* config/powerpc/spu-linux.mh: New file.
authorUlrich Weigand <uweigand@de.ibm.com>
Wed, 22 Nov 2006 13:49:53 +0000 (13:49 +0000)
committerUlrich Weigand <uweigand@de.ibm.com>
Wed, 22 Nov 2006 13:49:53 +0000 (13:49 +0000)
* config/spu/spu.mt: New file.
* configure.ac: Provide gdb_native configuration variable.
* configure: Regenerate.
* configure.host: Support powerpc64 to spu 'pseudo-native' mode.
* configure.tgt: Add "spu" target_cpu and "spu*-*-*" target.
* Makefile.in (spu_tdep_h): New variable.
(ALLDEPFILES): Add spu-linux-nat.c and spu-tdep.c
(spu-linux-nat.o, spu-tdep.o): Add dependencies.
* spu-linux-nat.c: New file.
* spu-tdep.c: New file.
* spu-tdep.h: New file.

gdb/ChangeLog
gdb/Makefile.in
gdb/config/powerpc/spu-linux.mh [new file with mode: 0644]
gdb/config/spu/spu.mt [new file with mode: 0644]
gdb/configure
gdb/configure.ac
gdb/configure.host
gdb/configure.tgt
gdb/spu-linux-nat.c [new file with mode: 0644]
gdb/spu-tdep.c [new file with mode: 0644]
gdb/spu-tdep.h [new file with mode: 0644]

index b3f4b41de57852ebbcccbd827231fb216ad2f5b8..881cddb339b9367f0353e762a4ca7aca660db51c 100644 (file)
@@ -1,3 +1,18 @@
+2006-11-22  Ulrich Weigand  <uweigand@de.ibm.com>
+
+       * config/powerpc/spu-linux.mh: New file.
+       * config/spu/spu.mt: New file.
+       * configure.ac: Provide gdb_native configuration variable.
+       * configure: Regenerate.
+       * configure.host: Support powerpc64 to spu 'pseudo-native' mode.
+       * configure.tgt: Add "spu" target_cpu and "spu*-*-*" target.
+       * Makefile.in (spu_tdep_h): New variable.
+       (ALLDEPFILES): Add spu-linux-nat.c and spu-tdep.c
+       (spu-linux-nat.o, spu-tdep.o): Add dependencies.
+       * spu-linux-nat.c: New file.
+       * spu-tdep.c: New file.
+       * spu-tdep.h: New file.
+
 2006-11-22  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * findvar.c (address_from_register): New function.
index 869dd7902520cbe4c45a1fa01d3442026126e63e..25a04a21e928790e9918c93c25b808ddc3c87ac0 100644 (file)
@@ -799,6 +799,7 @@ source_h = source.h
 sparc64_tdep_h = sparc64-tdep.h $(sparc_tdep_h)
 sparc_nat_h = sparc-nat.h
 sparc_tdep_h = sparc-tdep.h
+spu_tdep_h = spu-tdep.h
 srec_h = srec.h
 stabsread_h = stabsread.h
 stack_h = stack.h
@@ -1497,6 +1498,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 \
        v850-tdep.c \
        vax-nat.c vax-tdep.c vaxbsd-nat.c vaxnbsd-tdep.c \
        win32-nat.c \
@@ -2721,6 +2723,15 @@ sparc-tdep.o: sparc-tdep.c $(defs_h) $(arch_utils_h) $(dis_asm_h) \
        $(frame_unwind_h) $(gdbcore_h) $(gdbtypes_h) $(inferior_h) \
        $(symtab_h) $(objfiles_h) $(osabi_h) $(regcache_h) $(target_h) \
        $(value_h) $(gdb_assert_h) $(gdb_string_h) $(sparc_tdep_h)
+spu-linux-nat.o: spu-linux-nat.c $(defs_h) $(gdbcore_h) $(gdb_string_h) \
+       $(target_h) $(inferior_h) $(inf_ptrace_h) $(regcache_h) $(symfile_h) \
+       $(gdb_wait_h) $(spu_tdep_h)
+spu-tdep.o: spu-tdep.c $(defs_h) $(arch_utils_h) $(gdbtypes_h) $(gdbcmd_h) \
+       $(gdbcore_h) $(gdb_string_h) $(gdb_assert_h) $(frame_h) \
+       $(frame_unwind_h) $(frame_base_h) $(trad_frame_h) $(symtab_h) \
+       $(symfile_h) $(value_h) $(inferior_h) $(dis_asm_h) $(objfiles_h) \
+       $(language_h) $(regcache_h) $(reggroups_h) $(floatformat_h) \
+       $(spu_tdep_h)
 stabsread.o: stabsread.c $(defs_h) $(gdb_string_h) $(bfd_h) $(gdb_obstack_h) \
        $(symtab_h) $(gdbtypes_h) $(expression_h) $(symfile_h) $(objfiles_h) \
        $(aout_stab_gnu_h) $(libaout_h) $(aout_aout64_h) $(gdb_stabs_h) \
diff --git a/gdb/config/powerpc/spu-linux.mh b/gdb/config/powerpc/spu-linux.mh
new file mode 100644 (file)
index 0000000..068d294
--- /dev/null
@@ -0,0 +1,7 @@
+# Target: Cell BE (PowerPC64 + SPU)
+
+# This implements a 'pseudo-native' GDB running on the
+# PPU side of the Cell BE and debugging the SPU side.
+
+NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o
+
diff --git a/gdb/config/spu/spu.mt b/gdb/config/spu/spu.mt
new file mode 100644 (file)
index 0000000..2ea6fdc
--- /dev/null
@@ -0,0 +1,2 @@
+# Target: Cell BE SPU
+TDEPFILES= spu-tdep.o 
index e93346f122bc0c262f6c1a5e5b0282f44df7a95b..5aa226fad83021506644458cc7c299d6b157528e 100755 (executable)
@@ -3062,6 +3062,12 @@ subdirs="$subdirs doc testsuite"
 # configuration.
 gdb_host_obs=posix-hdep.o
 
+if test "${target}" = "${host}"; then
+  gdb_native=yes
+else
+  gdb_native=no
+fi
+
 . $srcdir/configure.host
 
 . $srcdir/configure.tgt
@@ -22335,7 +22341,7 @@ ac_x_header_dirs='
 /usr/openwin/share/include'
 
 if test "$ac_x_includes" = no; then
-  # Guess where to find include files, by looking for Intrinsic.h.
+  # Guess where to find include files, by looking for Xlib.h.
   # First, try using that file with no special directory specified.
   cat >conftest.$ac_ext <<_ACEOF
 /* confdefs.h.  */
@@ -22343,7 +22349,7 @@ _ACEOF
 cat confdefs.h >>conftest.$ac_ext
 cat >>conftest.$ac_ext <<_ACEOF
 /* end confdefs.h.  */
-#include <X11/Intrinsic.h>
+#include <X11/Xlib.h>
 _ACEOF
 if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
   (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
@@ -22370,7 +22376,7 @@ else
 sed 's/^/| /' conftest.$ac_ext >&5
 
   for ac_dir in $ac_x_header_dirs; do
-  if test -r "$ac_dir/X11/Intrinsic.h"; then
+  if test -r "$ac_dir/X11/Xlib.h"; then
     ac_x_includes=$ac_dir
     break
   fi
@@ -22391,11 +22397,11 @@ _ACEOF
 cat confdefs.h >>conftest.$ac_ext
 cat >>conftest.$ac_ext <<_ACEOF
 /* end confdefs.h.  */
-#include <X11/Intrinsic.h>
+#include <X11/Xlib.h>
 int
 main ()
 {
-XtMalloc (0)
+XrmInitialize ()
   ;
   return 0;
 }
@@ -22572,7 +22578,7 @@ fi
 
 
 frags=
-if test "${target}" = "${host}"; then
+if test "${gdb_native}" = "yes"; then
   host_makefile_frag=${srcdir}/config/${gdb_host_cpu}/${gdb_host}.mh
   if test ! -f ${host_makefile_frag}; then
     { { echo "$as_me:$LINENO: error: \"*** Gdb does not support native target ${host}\"" >&5
@@ -22604,7 +22610,7 @@ targetfile=`sed -n '
 s/DEPRECATED_TM_FILE[  ]*=[    ]*\([^  ]*\)/\1/p
 ' ${target_makefile_frag}`
 
-if test "${target}" = "${host}"; then
+if test "${gdb_native}" = "yes"; then
 # We pick this up from the host configuration file (.mh) because we
 # do not have a native configuration Makefile fragment.
 nativefile=`sed -n '
index 9b09b94ccb1e936c53ebf3f23e8cac8c0386f4bb..b80f7bd68ab8001ae146eb0ae4f2936ffd3759a3 100644 (file)
@@ -81,6 +81,12 @@ AC_CONFIG_SUBDIRS(doc testsuite)
 # configuration.
 gdb_host_obs=posix-hdep.o
 
+if test "${target}" = "${host}"; then
+  gdb_native=yes
+else
+  gdb_native=no
+fi
+
 . $srcdir/configure.host
 
 . $srcdir/configure.tgt
@@ -1420,7 +1426,7 @@ fi
 AC_SUBST(target_subdir)
 
 frags=
-if test "${target}" = "${host}"; then
+if test "${gdb_native}" = "yes"; then
   host_makefile_frag=${srcdir}/config/${gdb_host_cpu}/${gdb_host}.mh
   if test ! -f ${host_makefile_frag}; then
     AC_MSG_ERROR("*** Gdb does not support native target ${host}")
@@ -1449,7 +1455,7 @@ targetfile=`sed -n '
 s/DEPRECATED_TM_FILE[  ]*=[    ]*\([^  ]*\)/\1/p
 ' ${target_makefile_frag}`
 
-if test "${target}" = "${host}"; then
+if test "${gdb_native}" = "yes"; then
 # We pick this up from the host configuration file (.mh) because we
 # do not have a native configuration Makefile fragment.
 nativefile=`sed -n '
index bbfe4985f3d5dbcb3fbb813ddbb08de94c73622d..3615859cc21834fcb18c327dd5234b1fba731a6d 100644 (file)
@@ -114,7 +114,13 @@ powerpc-*-netbsd* | powerpc-*-knetbsd*-gnu)
                        gdb_host=nbsd ;;
 powerpc-*-openbsd*)    gdb_host=obsd ;;
 
-powerpc64-*-linux*)     gdb_host=ppc64-linux ;;
+powerpc64-*-linux*)     gdb_host=ppc64-linux
+                        # Support 'pseudo-native' debugging on the Cell BE
+                        if test "${target_cpu}" = "spu"; then
+                               gdb_host=spu-linux
+                               gdb_native=yes
+                        fi
+                       ;;
 
 rs6000-*-lynxos*)      gdb_host=rs6000lynx ;;
 rs6000-*-aix4*)                gdb_host=aix4 ;;
index 45ff7e2f319fe07b2a8bf1af3e9431be453696b3..8c4ccb551f88df3b8c9220bd9eaef64439673279 100644 (file)
@@ -25,6 +25,7 @@ sparc*)                       gdb_target_cpu=sparc ;;
 thumb*)                        gdb_target_cpu=arm ;;
 s390*)                 gdb_target_cpu=s390 ;;
 sh*)                   gdb_target_cpu=sh ;;
+spu*)                  gdb_target_cpu=spu ;;
 strongarm*)            gdb_target_cpu=arm ;;
 xscale*)               gdb_target_cpu=arm ;;
 x86_64*)               gdb_target_cpu=i386 ;;
@@ -209,6 +210,8 @@ sparc-*-rtems*)             gdb_target=embed ;;
 sparc-*-*)             gdb_target=sparc ;;
 sparc64-*-*)           gdb_target=sparc64 ;;
 
+spu*-*-*)              gdb_target=spu ;;
+
 xstormy16-*-*)          gdb_target=xstormy16 ;;
 
 v850*-*-elf)           gdb_target=v850 ;;
diff --git a/gdb/spu-linux-nat.c b/gdb/spu-linux-nat.c
new file mode 100644 (file)
index 0000000..fd3673c
--- /dev/null
@@ -0,0 +1,557 @@
+/* SPU native-dependent code for GDB, the GNU debugger.
+   Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include "target.h"
+#include "inferior.h"
+#include "inf-ptrace.h"
+#include "regcache.h"
+#include "symfile.h"
+#include "gdb_wait.h"
+
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include "spu-tdep.h"
+
+/* PPU side system calls.  */
+#define INSTR_SC       0x44000002
+#define NR_spu_run     0x0116
+
+
+/* Fetch PPU register REGNO.  */
+static CORE_ADDR
+fetch_ppc_register (int regno)
+{
+  PTRACE_TYPE_RET res;
+
+  int tid = TIDGET (inferior_ptid);
+  if (tid == 0)
+    tid = PIDGET (inferior_ptid);
+
+#ifndef __powerpc64__
+  /* If running as a 32-bit process on a 64-bit system, we attempt
+     to get the full 64-bit register content of the target process.
+     If the PPC special ptrace call fails, we're on a 32-bit system;
+     just fall through to the regular ptrace call in that case.  */
+  {
+    gdb_byte buf[8];
+
+    errno = 0;
+    ptrace (PPC_PTRACE_PEEKUSR_3264, tid,
+           (PTRACE_TYPE_ARG3) (regno * 8), buf);
+    if (errno == 0)
+      ptrace (PPC_PTRACE_PEEKUSR_3264, tid,
+             (PTRACE_TYPE_ARG3) (regno * 8 + 4), buf + 4);
+    if (errno == 0)
+      return (CORE_ADDR) *(unsigned long long *)buf;
+  }
+#endif
+
+  errno = 0;
+  res = ptrace (PT_READ_U, tid,
+               (PTRACE_TYPE_ARG3) (regno * sizeof (PTRACE_TYPE_RET)), 0);
+  if (errno != 0)
+    {
+      char mess[128];
+      xsnprintf (mess, sizeof mess, "reading PPC register #%d", regno);
+      perror_with_name (_(mess));
+    }
+
+  return (CORE_ADDR) (unsigned long) res;
+}
+
+/* Fetch WORD from PPU memory at (aligned) MEMADDR in thread TID.  */
+static int
+fetch_ppc_memory_1 (int tid, CORE_ADDR memaddr, PTRACE_TYPE_RET *word)
+{
+  errno = 0;
+
+#ifndef __powerpc64__
+  if (memaddr >> 32)
+    {
+      unsigned long long addr_8 = (unsigned long long) memaddr;
+      ptrace (PPC_PTRACE_PEEKTEXT_3264, tid, (PTRACE_TYPE_ARG3) &addr_8, word);
+    }
+  else
+#endif
+    *word = ptrace (PT_READ_I, tid, (PTRACE_TYPE_ARG3) (size_t) memaddr, 0);
+
+  return errno;
+}
+
+/* Store WORD into PPU memory at (aligned) MEMADDR in thread TID.  */
+static int
+store_ppc_memory_1 (int tid, CORE_ADDR memaddr, PTRACE_TYPE_RET word)
+{
+  errno = 0;
+
+#ifndef __powerpc64__
+  if (memaddr >> 32)
+    {
+      unsigned long long addr_8 = (unsigned long long) memaddr;
+      ptrace (PPC_PTRACE_POKEDATA_3264, tid, (PTRACE_TYPE_ARG3) &addr_8, word);
+    }
+  else
+#endif
+    ptrace (PT_WRITE_D, tid, (PTRACE_TYPE_ARG3) (size_t) memaddr, word);
+
+  return errno;
+}
+
+/* Fetch LEN bytes of PPU memory at MEMADDR to MYADDR.  */
+static int
+fetch_ppc_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+  int i, ret;
+
+  CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_TYPE_RET);
+  int count = ((((memaddr + len) - addr) + sizeof (PTRACE_TYPE_RET) - 1)
+              / sizeof (PTRACE_TYPE_RET));
+  PTRACE_TYPE_RET *buffer;
+
+  int tid = TIDGET (inferior_ptid);
+  if (tid == 0)
+    tid = PIDGET (inferior_ptid);
+
+  buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+  for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
+    if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[i])) != 0)
+      return ret;
+
+  memcpy (myaddr,
+         (char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
+         len);
+
+  return 0;
+}
+
+/* Store LEN bytes from MYADDR to PPU memory at MEMADDR.  */
+static int
+store_ppc_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+  int i, ret;
+
+  CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_TYPE_RET);
+  int count = ((((memaddr + len) - addr) + sizeof (PTRACE_TYPE_RET) - 1)
+              / sizeof (PTRACE_TYPE_RET));
+  PTRACE_TYPE_RET *buffer;
+
+  int tid = TIDGET (inferior_ptid);
+  if (tid == 0)
+    tid = PIDGET (inferior_ptid);
+
+  buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+
+  if (addr != memaddr || len < (int) sizeof (PTRACE_TYPE_RET))
+    if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[0])) != 0)
+      return ret;
+
+  if (count > 1)
+    if ((ret = fetch_ppc_memory_1 (tid, addr + (count - 1)
+                                              * sizeof (PTRACE_TYPE_RET),
+                                  &buffer[count - 1])) != 0)
+      return ret;
+
+  memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
+          myaddr, len);
+
+  for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
+    if ((ret = store_ppc_memory_1 (tid, addr, buffer[i])) != 0)
+      return ret;
+
+  return 0;
+}
+
+
+/* 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 (int *fd, CORE_ADDR *addr)
+{
+  gdb_byte buf[4];
+  CORE_ADDR pc = fetch_ppc_register (32);  /* nip */
+
+  /* Fetch instruction preceding current NIP.  */
+  if (fetch_ppc_memory (pc-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.  */
+  if (fetch_ppc_register (0) != NR_spu_run)
+    return 0;
+
+  /* Register 3 contains fd, register 4 the NPC param pointer.  */
+  *fd = fetch_ppc_register (34);  /* orig_gpr3 */
+  *addr = fetch_ppc_register (4);
+  return 1;
+}
+
+
+/* Copy LEN bytes at OFFSET in spufs file ANNEX into/from READBUF or WRITEBUF,
+   using the /proc file system.  */
+static LONGEST
+spu_proc_xfer_spu (const char *annex, gdb_byte *readbuf,
+                  const gdb_byte *writebuf,
+                  ULONGEST offset, LONGEST len)
+{
+  char buf[128];
+  int fd = 0;
+  int ret = -1;
+  int pid = PIDGET (inferior_ptid);
+
+  if (!annex)
+    return 0;
+
+  xsnprintf (buf, sizeof buf, "/proc/%d/fd/%s", pid, annex);
+  fd = open (buf, writebuf? O_WRONLY : O_RDONLY);
+  if (fd <= 0)
+    return -1;
+
+  if (offset != 0
+      && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+    {
+      close (fd);
+      return -1;
+    }
+
+  if (writebuf)
+    ret = write (fd, writebuf, (size_t) len);
+  else if (readbuf)
+    ret = read (fd, readbuf, (size_t) len);
+
+  close (fd);
+  return ret;
+}
+
+
+/* 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 (struct bfd *nbfd, void *open_closure)
+{
+  return open_closure;
+}
+
+static int
+spu_bfd_iovec_close (struct bfd *nbfd, void *stream)
+{
+  xfree (stream);
+  return 1;
+}
+
+static file_ptr
+spu_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+                    file_ptr nbytes, file_ptr offset)
+{
+  CORE_ADDR addr = *(CORE_ADDR *)stream;
+
+  if (fetch_ppc_memory (addr + offset, buf, nbytes) != 0)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return -1;
+    }
+
+  return nbytes;
+}
+
+static bfd *
+spu_bfd_open (CORE_ADDR addr)
+{
+  struct bfd *nbfd;
+
+  CORE_ADDR *open_closure = xmalloc (sizeof (CORE_ADDR));
+  *open_closure = addr;
+
+  nbfd = bfd_openr_iovec (xstrdup ("<in-memory>"), "elf32-spu",
+                         spu_bfd_iovec_open, open_closure,
+                         spu_bfd_iovec_pread, spu_bfd_iovec_close);
+  if (!nbfd)
+    return NULL;
+
+  if (!bfd_check_format (nbfd, bfd_object))
+    {
+      bfd_close (nbfd);
+      return NULL;
+    }
+
+  return nbfd;
+}
+
+/* INFERIOR_FD is a file handle passed by the inferior to the
+   spu_run system call.  Assuming the SPE context was allocated
+   by the libspe library, try to retrieve the main SPE executable
+   file from its copy within the target process.  */
+static void
+spu_symbol_file_add_from_memory (int inferior_fd)
+{
+  CORE_ADDR addr;
+  struct bfd *nbfd;
+
+  char id[128];
+  char annex[32];
+  int len;
+
+  /* Read object ID.  */
+  xsnprintf (annex, sizeof annex, "%d/object-id", inferior_fd);
+  len = spu_proc_xfer_spu (annex, id, NULL, 0, sizeof id);
+  if (len <= 0 || len >= sizeof id)
+    return;
+  id[len] = 0;
+  if (sscanf (id, "0x%llx", &addr) != 1)
+    return;
+
+  /* Open BFD representing SPE executable and read its symbols.  */
+  nbfd = spu_bfd_open (addr);
+  if (nbfd)
+    symbol_file_add_from_bfd (nbfd, 0, NULL, 1, 0);
+}
+
+
+/* Override the post_startup_inferior routine to continue running
+   the inferior until the first spu_run system call.  */
+static void
+spu_child_post_startup_inferior (ptid_t ptid)
+{
+  int fd;
+  CORE_ADDR addr;
+
+  int tid = TIDGET (ptid);
+  if (tid == 0)
+    tid = PIDGET (ptid);
+  
+  while (!parse_spufs_run (&fd, &addr))
+    {
+      ptrace (PT_SYSCALL, tid, (PTRACE_TYPE_ARG3) 0, 0);
+      waitpid (tid, NULL, __WALL | __WNOTHREAD);
+    }
+}
+
+/* Override the post_attach routine to try load the SPE executable
+   file image from its copy inside the target process.  */
+static void
+spu_child_post_attach (int pid)
+{
+  int fd;
+  CORE_ADDR addr;
+
+  /* Like child_post_startup_inferior, if we happened to attach to
+     the inferior while it wasn't currently in spu_run, continue 
+     running it until we get back there.  */
+  while (!parse_spufs_run (&fd, &addr))
+    {
+      ptrace (PT_SYSCALL, pid, (PTRACE_TYPE_ARG3) 0, 0);
+      waitpid (pid, NULL, __WALL | __WNOTHREAD);
+    }
+
+  /* If the user has not provided an executable file, try to extract
+     the image from inside the target process.  */
+  if (!get_exec_file (0))
+    spu_symbol_file_add_from_memory (fd);
+}
+
+/* Wait for child PTID to do something.  Return id of the child,
+   minus_one_ptid in case of error; store status into *OURSTATUS.  */
+static ptid_t
+spu_child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+  int save_errno;
+  int status;
+  pid_t pid;
+
+  do
+    {
+      set_sigint_trap ();      /* Causes SIGINT to be passed on to the
+                                  attached process.  */
+      set_sigio_trap ();
+
+      pid = waitpid (PIDGET (ptid), &status, 0);
+      if (pid == -1 && errno == ECHILD)
+       /* Try again with __WCLONE to check cloned processes.  */
+       pid = waitpid (PIDGET (ptid), &status, __WCLONE);
+
+      save_errno = errno;
+
+      /* Make sure we don't report an event for the exit of the
+         original program, if we've detached from it.  */
+      if (pid != -1 && !WIFSTOPPED (status) && pid != PIDGET (inferior_ptid))
+       {
+         pid = -1;
+         save_errno = EINTR;
+       }
+
+      clear_sigio_trap ();
+      clear_sigint_trap ();
+    }
+  while (pid == -1 && save_errno == EINTR);
+
+  if (pid == -1)
+    {
+      warning ("Child process unexpectedly missing: %s",
+              safe_strerror (save_errno));
+
+      /* Claim it exited with unknown signal.  */
+      ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+      ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+      return minus_one_ptid;
+    }
+
+  store_waitstatus (ourstatus, status);
+  return pid_to_ptid (pid);
+}
+
+/* Override the fetch_inferior_register routine.  */
+static void
+spu_fetch_inferior_registers (int regno)
+{
+  int fd;
+  CORE_ADDR addr;
+
+  /* We must be stopped on a spu_run system call.  */
+  if (!parse_spufs_run (&fd, &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, fd);
+      regcache_raw_supply (current_regcache, SPU_ID_REGNUM, buf);
+    }
+
+  /* The NPC register is found at ADDR.  */
+  if (regno == -1 || regno == SPU_PC_REGNUM)
+    {
+      gdb_byte buf[4];
+      if (fetch_ppc_memory (addr, buf, 4) == 0)
+       regcache_raw_supply (current_regcache, SPU_PC_REGNUM, buf);
+    }
+
+  /* The GPRs are found in the "regs" spufs file.  */
+  if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+    {
+      gdb_byte buf[16 * SPU_NUM_GPRS];
+      char annex[32];
+      int i;
+
+      xsnprintf (annex, sizeof annex, "%d/regs", fd);
+      if (spu_proc_xfer_spu (annex, buf, NULL, 0, sizeof buf) == sizeof buf)
+       for (i = 0; i < SPU_NUM_GPRS; i++)
+         regcache_raw_supply (current_regcache, i, buf + i*16);
+    }
+}
+
+/* Override the store_inferior_register routine.  */
+static void
+spu_store_inferior_registers (int regno)
+{
+  int fd;
+  CORE_ADDR addr;
+
+  /* We must be stopped on a spu_run system call.  */
+  if (!parse_spufs_run (&fd, &addr))
+    return;
+
+  /* The NPC register is found at ADDR.  */
+  if (regno == -1 || regno == SPU_PC_REGNUM)
+    {
+      gdb_byte buf[4];
+      regcache_raw_collect (current_regcache, SPU_PC_REGNUM, buf);
+      store_ppc_memory (addr, buf, 4);
+    }
+
+  /* The GPRs are found in the "regs" spufs file.  */
+  if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+    {
+      gdb_byte buf[16 * SPU_NUM_GPRS];
+      char annex[32];
+      int i;
+
+      for (i = 0; i < SPU_NUM_GPRS; i++)
+       regcache_raw_collect (current_regcache, i, buf + i*16);
+
+      xsnprintf (annex, sizeof annex, "%d/regs", fd);
+      spu_proc_xfer_spu (annex, NULL, 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)
+{
+  if (object == TARGET_OBJECT_MEMORY)
+    {
+      int fd;
+      CORE_ADDR addr;
+      char mem_annex[32];
+
+      /* We must be stopped on a spu_run system call.  */
+      if (!parse_spufs_run (&fd, &addr))
+       return 0;
+
+      /* Use the "mem" spufs file to access SPU local store.  */
+      xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd);
+      return spu_proc_xfer_spu (mem_annex, readbuf, writebuf, offset, len);
+    }
+
+  return 0;
+}
+
+/* Override the to_can_use_hw_breakpoint routine.  */
+static int
+spu_can_use_hw_breakpoint (int type, int cnt, int othertype)
+{
+  return 0;
+}
+
+
+/* Initialize SPU native target.  */
+void 
+_initialize_spu_nat (void)
+{
+  /* Generic ptrace methods.  */
+  struct target_ops *t;
+  t = inf_ptrace_target ();
+
+  /* Add SPU methods.  */
+  t->to_post_attach = spu_child_post_attach;  
+  t->to_post_startup_inferior = spu_child_post_startup_inferior;
+  t->to_wait = spu_child_wait;
+  t->to_fetch_registers = spu_fetch_inferior_registers;
+  t->to_store_registers = spu_store_inferior_registers;
+  t->to_xfer_partial = spu_xfer_partial;
+  t->to_can_use_hw_breakpoint = spu_can_use_hw_breakpoint;
+
+  /* Register SPU target.  */
+  add_target (t);
+}
+
diff --git a/gdb/spu-tdep.c b/gdb/spu-tdep.c
new file mode 100644 (file)
index 0000000..ed88dd4
--- /dev/null
@@ -0,0 +1,1099 @@
+/* SPU target-dependent code for GDB, the GNU debugger.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+   Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
+   Based on a port by Sid Manning <sid@us.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., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include "gdb_assert.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "trad-frame.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "value.h"
+#include "inferior.h"
+#include "dis-asm.h"
+#include "objfiles.h"
+#include "language.h"
+#include "regcache.h"
+#include "reggroups.h"
+#include "floatformat.h"
+
+#include "spu-tdep.h"
+
+
+/* Registers.  */
+
+static const char *
+spu_register_name (int reg_nr)
+{
+  static char *register_names[] = 
+    {
+      "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+      "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+      "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+      "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+      "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39",
+      "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47",
+      "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55",
+      "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63",
+      "r64", "r65", "r66", "r67", "r68", "r69", "r70", "r71",
+      "r72", "r73", "r74", "r75", "r76", "r77", "r78", "r79",
+      "r80", "r81", "r82", "r83", "r84", "r85", "r86", "r87",
+      "r88", "r89", "r90", "r91", "r92", "r93", "r94", "r95",
+      "r96", "r97", "r98", "r99", "r100", "r101", "r102", "r103",
+      "r104", "r105", "r106", "r107", "r108", "r109", "r110", "r111",
+      "r112", "r113", "r114", "r115", "r116", "r117", "r118", "r119",
+      "r120", "r121", "r122", "r123", "r124", "r125", "r126", "r127",
+      "id", "pc", "sp"
+    };
+
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= sizeof register_names / sizeof *register_names)
+    return NULL;
+
+  return register_names[reg_nr];
+}
+
+static struct type *
+spu_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < SPU_NUM_GPRS)
+    return builtin_type_vec128;
+
+  switch (reg_nr)
+    {
+    case SPU_ID_REGNUM:
+      return builtin_type_uint32;
+
+    case SPU_PC_REGNUM:
+      return builtin_type_void_func_ptr;
+
+    case SPU_SP_REGNUM:
+      return builtin_type_void_data_ptr;
+
+    default:
+      internal_error (__FILE__, __LINE__, "invalid regnum");
+    }
+}
+
+/* Pseudo registers for preferred slots - stack pointer.  */
+
+static void
+spu_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
+                          int regnum, gdb_byte *buf)
+{
+  gdb_byte reg[16];
+
+  switch (regnum)
+    {
+    case SPU_SP_REGNUM:
+      regcache_raw_read (regcache, SPU_RAW_SP_REGNUM, reg);
+      memcpy (buf, reg, 4);
+      break;
+
+    default:
+      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+    }
+}
+
+static void
+spu_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
+                           int regnum, const gdb_byte *buf)
+{
+  gdb_byte reg[16];
+
+  switch (regnum)
+    {
+    case SPU_SP_REGNUM:
+      regcache_raw_read (regcache, SPU_RAW_SP_REGNUM, reg);
+      memcpy (reg, buf, 4);
+      regcache_raw_write (regcache, SPU_RAW_SP_REGNUM, reg);
+      break;
+
+    default:
+      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+    }
+}
+
+/* Value conversion -- access scalar values at the preferred slot.  */
+
+static int
+spu_convert_register_p (int regno, struct type *type)
+{
+  return regno < SPU_NUM_GPRS && TYPE_LENGTH (type) < 16;
+}
+
+static void
+spu_register_to_value (struct frame_info *frame, int regnum,
+                       struct type *valtype, gdb_byte *out)
+{
+  gdb_byte in[16];
+  int len = TYPE_LENGTH (valtype);
+  int preferred_slot = len < 4 ? 4 - len : 0;
+  gdb_assert (len < 16);
+
+  get_frame_register (frame, regnum, in);
+  memcpy (out, in + preferred_slot, len);
+}
+
+static void
+spu_value_to_register (struct frame_info *frame, int regnum,
+                       struct type *valtype, const gdb_byte *in)
+{
+  gdb_byte out[16];
+  int len = TYPE_LENGTH (valtype);
+  int preferred_slot = len < 4 ? 4 - len : 0;
+  gdb_assert (len < 16);
+
+  memset (out, 0, 16);
+  memcpy (out + preferred_slot, in, len);
+  put_frame_register (frame, regnum, out);
+}
+
+/* Register groups.  */
+
+static int
+spu_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+                        struct reggroup *group)
+{
+  /* Registers displayed via 'info regs'.  */
+  if (group == general_reggroup)
+    return 1;
+
+  /* Registers displayed via 'info float'.  */
+  if (group == float_reggroup)
+    return 0;
+
+  /* Registers that need to be saved/restored in order to
+     push or pop frames.  */
+  if (group == save_reggroup || group == restore_reggroup)
+    return 1;
+
+  return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+
+/* Decoding SPU instructions.  */
+
+enum
+  {
+    op_lqd   = 0x34,
+    op_lqx   = 0x3c4,
+    op_lqa   = 0x61,
+    op_lqr   = 0x67,
+    op_stqd  = 0x24,
+    op_stqx  = 0x144,
+    op_stqa  = 0x41,
+    op_stqr  = 0x47,
+
+    op_il    = 0x081,
+    op_ila   = 0x21,
+    op_a     = 0x0c0,
+    op_ai    = 0x1c,
+
+    op_selb  = 0x4,
+
+    op_br    = 0x64,
+    op_bra   = 0x60,
+    op_brsl  = 0x66,
+    op_brasl = 0x62,
+    op_brnz  = 0x42,
+    op_brz   = 0x40,
+    op_brhnz = 0x46,
+    op_brhz  = 0x44,
+    op_bi    = 0x1a8,
+    op_bisl  = 0x1a9,
+    op_biz   = 0x128,
+    op_binz  = 0x129,
+    op_bihz  = 0x12a,
+    op_bihnz = 0x12b,
+  };
+
+static int
+is_rr (unsigned int insn, int op, int *rt, int *ra, int *rb)
+{
+  if ((insn >> 21) == op)
+    {
+      *rt = insn & 127;
+      *ra = (insn >> 7) & 127;
+      *rb = (insn >> 14) & 127;
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+is_rrr (unsigned int insn, int op, int *rt, int *ra, int *rb, int *rc)
+{
+  if ((insn >> 28) == op)
+    {
+      *rt = (insn >> 21) & 127;
+      *ra = (insn >> 7) & 127;
+      *rb = (insn >> 14) & 127;
+      *rc = insn & 127;
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+is_ri7 (unsigned int insn, int op, int *rt, int *ra, int *i7)
+{
+  if ((insn >> 21) == op)
+    {
+      *rt = insn & 127;
+      *ra = (insn >> 7) & 127;
+      *i7 = (((insn >> 14) & 127) ^ 0x40) - 0x40;
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+is_ri10 (unsigned int insn, int op, int *rt, int *ra, int *i10)
+{
+  if ((insn >> 24) == op)
+    {
+      *rt = insn & 127;
+      *ra = (insn >> 7) & 127;
+      *i10 = (((insn >> 14) & 0x3ff) ^ 0x200) - 0x200;
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+is_ri16 (unsigned int insn, int op, int *rt, int *i16)
+{
+  if ((insn >> 23) == op)
+    {
+      *rt = insn & 127;
+      *i16 = (((insn >> 7) & 0xffff) ^ 0x8000) - 0x8000;
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+is_ri18 (unsigned int insn, int op, int *rt, int *i18)
+{
+  if ((insn >> 25) == op)
+    {
+      *rt = insn & 127;
+      *i18 = (((insn >> 7) & 0x3ffff) ^ 0x20000) - 0x20000;
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+is_branch (unsigned int insn, int *offset, int *reg)
+{
+  int rt, i7, i16;
+
+  if (is_ri16 (insn, op_br, &rt, &i16)
+      || is_ri16 (insn, op_brsl, &rt, &i16)
+      || is_ri16 (insn, op_brnz, &rt, &i16)
+      || is_ri16 (insn, op_brz, &rt, &i16)
+      || is_ri16 (insn, op_brhnz, &rt, &i16)
+      || is_ri16 (insn, op_brhz, &rt, &i16))
+    {
+      *reg = SPU_PC_REGNUM;
+      *offset = i16 << 2;
+      return 1;
+    }
+
+  if (is_ri16 (insn, op_bra, &rt, &i16)
+      || is_ri16 (insn, op_brasl, &rt, &i16))
+    {
+      *reg = -1;
+      *offset = i16 << 2;
+      return 1;
+    }
+
+  if (is_ri7 (insn, op_bi, &rt, reg, &i7)
+      || is_ri7 (insn, op_bisl, &rt, reg, &i7)
+      || is_ri7 (insn, op_biz, &rt, reg, &i7)
+      || is_ri7 (insn, op_binz, &rt, reg, &i7)
+      || is_ri7 (insn, op_bihz, &rt, reg, &i7)
+      || is_ri7 (insn, op_bihnz, &rt, reg, &i7))
+    {
+      *offset = 0;
+      return 1;
+    }
+
+  return 0;
+}
+
+
+/* Prolog parsing.  */
+
+struct spu_prologue_data
+  {
+    /* Stack frame size.  -1 if analysis was unsuccessful.  */
+    int size;
+
+    /* How to find the CFA.  The CFA is equal to SP at function entry.  */
+    int cfa_reg;
+    int cfa_offset;
+
+    /* Offset relative to CFA where a register is saved.  -1 if invalid.  */
+    int reg_offset[SPU_NUM_GPRS];
+  };
+
+static CORE_ADDR
+spu_analyze_prologue (CORE_ADDR start_pc, CORE_ADDR end_pc,
+                      struct spu_prologue_data *data)
+{
+  int found_sp = 0;
+  int found_fp = 0;
+  int found_lr = 0;
+  int reg_immed[SPU_NUM_GPRS];
+  gdb_byte buf[16];
+  CORE_ADDR prolog_pc = start_pc;
+  CORE_ADDR pc;
+  int i;
+
+
+  /* Initialize DATA to default values.  */
+  data->size = -1;
+
+  data->cfa_reg = SPU_RAW_SP_REGNUM;
+  data->cfa_offset = 0;
+
+  for (i = 0; i < SPU_NUM_GPRS; i++)
+    data->reg_offset[i] = -1;
+
+  /* Set up REG_IMMED array.  This is non-zero for a register if we know its
+     preferred slot currently holds this immediate value.  */
+  for (i = 0; i < SPU_NUM_GPRS; i++)
+      reg_immed[i] = 0;
+
+  /* Scan instructions until the first branch.
+
+     The following instructions are important prolog components:
+
+       - The first instruction to set up the stack pointer.
+       - The first instruction to set up the frame pointer.
+       - The first instruction to save the link register.
+
+     We return the instruction after the latest of these three,
+     or the incoming PC if none is found.  The first instruction
+     to set up the stack pointer also defines the frame size.
+
+     Note that instructions saving incoming arguments to their stack
+     slots are not counted as important, because they are hard to
+     identify with certainty.  This should not matter much, because
+     arguments are relevant only in code compiled with debug data,
+     and in such code the GDB core will advance until the first source
+     line anyway, using SAL data.
+
+     For purposes of stack unwinding, we analyze the following types
+     of instructions in addition:
+
+      - Any instruction adding to the current frame pointer.
+      - Any instruction loading an immediate constant into a register.
+      - Any instruction storing a register onto the stack.
+
+     These are used to compute the CFA and REG_OFFSET output.  */
+
+  for (pc = start_pc; pc < end_pc; pc += 4)
+    {
+      unsigned int insn;
+      int rt, ra, rb, rc, immed;
+
+      if (target_read_memory (pc, buf, 4))
+       break;
+      insn = extract_unsigned_integer (buf, 4);
+
+      /* AI is the typical instruction to set up a stack frame.
+         It is also used to initialize the frame pointer.  */
+      if (is_ri10 (insn, op_ai, &rt, &ra, &immed))
+       {
+         if (rt == data->cfa_reg && ra == data->cfa_reg)
+           data->cfa_offset -= immed;
+
+         if (rt == SPU_RAW_SP_REGNUM && ra == SPU_RAW_SP_REGNUM
+             && !found_sp)
+           {
+             found_sp = 1;
+             prolog_pc = pc + 4;
+
+             data->size = -immed;
+           }
+         else if (rt == SPU_FP_REGNUM && ra == SPU_RAW_SP_REGNUM
+                  && !found_fp)
+           {
+             found_fp = 1;
+             prolog_pc = pc + 4;
+
+             data->cfa_reg = SPU_FP_REGNUM;
+             data->cfa_offset -= immed;
+           }
+       }
+
+      /* A is used to set up stack frames of size >= 512 bytes.
+         If we have tracked the contents of the addend register,
+         we can handle this as well.  */
+      else if (is_rr (insn, op_a, &rt, &ra, &rb))
+       {
+         if (rt == data->cfa_reg && ra == data->cfa_reg)
+           {
+             if (reg_immed[rb] != 0)
+               data->cfa_offset -= reg_immed[rb];
+             else
+               data->cfa_reg = -1;  /* We don't know the CFA any more.  */
+           }
+
+         if (rt == SPU_RAW_SP_REGNUM && ra == SPU_RAW_SP_REGNUM
+             && !found_sp)
+           {
+             found_sp = 1;
+             prolog_pc = pc + 4;
+
+             if (reg_immed[rb] != 0)
+               data->size = -reg_immed[rb];
+           }
+       }
+
+      /* We need to track IL and ILA used to load immediate constants
+         in case they are later used as input to an A instruction.  */
+      else if (is_ri16 (insn, op_il, &rt, &immed))
+       {
+         reg_immed[rt] = immed;
+       }
+
+      else if (is_ri18 (insn, op_ila, &rt, &immed))
+       {
+         reg_immed[rt] = immed & 0x3ffff;
+       }
+
+      /* STQD is used to save registers to the stack.  */
+      else if (is_ri10 (insn, op_stqd, &rt, &ra, &immed))
+       {
+         if (ra == data->cfa_reg)
+           data->reg_offset[rt] = data->cfa_offset - (immed << 4);
+
+         if (ra == data->cfa_reg && rt == SPU_LR_REGNUM
+              && !found_lr)
+           {
+             found_lr = 1;
+             prolog_pc = pc + 4;
+           }
+       }
+
+      /* _start uses SELB to set up the stack pointer.  */
+      else if (is_rrr (insn, op_selb, &rt, &ra, &rb, &rc))
+       {
+         if (rt == SPU_RAW_SP_REGNUM && !found_sp)
+           found_sp = 1;
+       }
+
+      /* We terminate if we find a branch.  */
+      else if (is_branch (insn, &immed, &ra))
+       break;
+    }
+
+
+  /* If we successfully parsed until here, and didn't find any instruction
+     modifying SP, we assume we have a frameless function.  */
+  if (!found_sp)
+    data->size = 0;
+
+  /* Return cooked instead of raw SP.  */
+  if (data->cfa_reg == SPU_RAW_SP_REGNUM)
+    data->cfa_reg = SPU_SP_REGNUM;
+
+  return prolog_pc;
+}
+
+/* Return the first instruction after the prologue starting at PC.  */
+static CORE_ADDR
+spu_skip_prologue (CORE_ADDR pc)
+{
+  struct spu_prologue_data data;
+  return spu_analyze_prologue (pc, (CORE_ADDR)-1, &data);
+}
+
+/* Return the frame pointer in use at address PC.  */
+static void
+spu_virtual_frame_pointer (CORE_ADDR pc, int *reg, LONGEST *offset)
+{
+  struct spu_prologue_data data;
+  spu_analyze_prologue (pc, (CORE_ADDR)-1, &data);
+
+  if (data.size != -1 && data.cfa_reg != -1)
+    {
+      /* The 'frame pointer' address is CFA minus frame size.  */
+      *reg = data.cfa_reg;
+      *offset = data.cfa_offset - data.size;
+    }
+  else
+    {
+      /* ??? We don't really know ... */
+      *reg = SPU_SP_REGNUM;
+      *offset = 0;
+    }
+}
+
+/* Normal stack frames.  */
+
+struct spu_unwind_cache
+{
+  CORE_ADDR func;
+  CORE_ADDR frame_base;
+  CORE_ADDR local_base;
+
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct spu_unwind_cache *
+spu_frame_unwind_cache (struct frame_info *next_frame,
+                       void **this_prologue_cache)
+{
+  struct spu_unwind_cache *info;
+  struct spu_prologue_data data;
+
+  if (*this_prologue_cache)
+    return *this_prologue_cache;
+
+  info = FRAME_OBSTACK_ZALLOC (struct spu_unwind_cache);
+  *this_prologue_cache = info;
+  info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+  info->frame_base = 0;
+  info->local_base = 0;
+
+  /* Find the start of the current function, and analyze its prologue.  */
+  info->func = frame_func_unwind (next_frame);
+  if (info->func == 0)
+    {
+      /* Fall back to using the current PC as frame ID.  */
+      info->func = frame_pc_unwind (next_frame);
+      data.size = -1;
+    }
+  else
+    spu_analyze_prologue (info->func, frame_pc_unwind (next_frame), &data);
+
+
+  /* If successful, use prologue analysis data.  */
+  if (data.size != -1 && data.cfa_reg != -1)
+    {
+      CORE_ADDR cfa;
+      int i;
+      gdb_byte buf[16];
+
+      /* Determine CFA via unwound CFA_REG plus CFA_OFFSET.  */
+      frame_unwind_register (next_frame, data.cfa_reg, buf);
+      cfa = extract_unsigned_integer (buf, 4) + data.cfa_offset;
+
+      /* Call-saved register slots.  */
+      for (i = 0; i < SPU_NUM_GPRS; i++)
+       if (i == SPU_LR_REGNUM
+           || (i >= SPU_SAVED1_REGNUM && i <= SPU_SAVEDN_REGNUM))
+         if (data.reg_offset[i] != -1)
+           info->saved_regs[i].addr = cfa - data.reg_offset[i];
+
+      /* The previous PC comes from the link register.  */
+      if (trad_frame_addr_p (info->saved_regs, SPU_LR_REGNUM))
+       info->saved_regs[SPU_PC_REGNUM] = info->saved_regs[SPU_LR_REGNUM];
+      else
+       info->saved_regs[SPU_PC_REGNUM].realreg = SPU_LR_REGNUM;
+
+      /* The previous SP is equal to the CFA.  */
+      trad_frame_set_value (info->saved_regs, SPU_SP_REGNUM, cfa);
+
+      /* Frame bases.  */
+      info->frame_base = cfa;
+      info->local_base = cfa - data.size;
+    }
+
+  /* Otherwise, fall back to reading the backchain link.  */
+  else
+    {
+      CORE_ADDR reg, backchain;
+
+      /* Get the backchain.  */
+      reg = frame_unwind_register_unsigned (next_frame, SPU_SP_REGNUM);
+      backchain = read_memory_unsigned_integer (reg, 4);
+
+      /* A zero backchain terminates the frame chain.  Also, sanity
+         check against the local store size limit.  */
+      if (backchain != 0 && backchain < SPU_LS_SIZE)
+       {
+         /* Assume the link register is saved into its slot.  */
+         if (backchain + 16 < SPU_LS_SIZE)
+           info->saved_regs[SPU_LR_REGNUM].addr = backchain + 16;
+
+         /* This will also be the previous PC.  */
+         if (trad_frame_addr_p (info->saved_regs, SPU_LR_REGNUM))
+           info->saved_regs[SPU_PC_REGNUM] = info->saved_regs[SPU_LR_REGNUM];
+         else
+           info->saved_regs[SPU_PC_REGNUM].realreg = SPU_LR_REGNUM;
+
+         /* The previous SP will equal the backchain value.  */
+         trad_frame_set_value (info->saved_regs, SPU_SP_REGNUM, backchain);
+
+          /* Frame bases.  */
+         info->frame_base = backchain;
+         info->local_base = reg;
+       }
+    }
+  return info;
+}
+
+static void
+spu_frame_this_id (struct frame_info *next_frame,
+                  void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct spu_unwind_cache *info =
+    spu_frame_unwind_cache (next_frame, this_prologue_cache);
+
+  if (info->frame_base == 0)
+    return;
+
+  *this_id = frame_id_build (info->frame_base, info->func);
+}
+
+static void
+spu_frame_prev_register (struct frame_info *next_frame,
+                        void **this_prologue_cache,
+                        int regnum, int *optimizedp,
+                        enum lval_type *lvalp, CORE_ADDR * addrp,
+                        int *realnump, gdb_byte *bufferp)
+{
+  struct spu_unwind_cache *info
+    = spu_frame_unwind_cache (next_frame, this_prologue_cache);
+
+  /* Special-case the stack pointer.  */
+  if (regnum == SPU_RAW_SP_REGNUM)
+    regnum = SPU_SP_REGNUM;
+
+  trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
+                               optimizedp, lvalp, addrp, realnump, bufferp);
+}
+
+static const struct frame_unwind spu_frame_unwind = {
+  NORMAL_FRAME,
+  spu_frame_this_id,
+  spu_frame_prev_register
+};
+
+const struct frame_unwind *
+spu_frame_sniffer (struct frame_info *next_frame)
+{
+  return &spu_frame_unwind;
+}
+
+static CORE_ADDR
+spu_frame_base_address (struct frame_info *next_frame, void **this_cache)
+{
+  struct spu_unwind_cache *info
+    = spu_frame_unwind_cache (next_frame, this_cache);
+  return info->local_base;
+}
+
+static const struct frame_base spu_frame_base = {
+  &spu_frame_unwind,
+  spu_frame_base_address,
+  spu_frame_base_address,
+  spu_frame_base_address
+};
+
+static CORE_ADDR
+spu_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, SPU_PC_REGNUM);
+}
+
+static CORE_ADDR
+spu_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, SPU_SP_REGNUM);
+}
+
+
+/* Function calling convention.  */
+
+static int
+spu_scalar_value_p (struct type *type)
+{
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_INT:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_RANGE:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_BOOL:
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_REF:
+      return TYPE_LENGTH (type) <= 16;
+
+    default:
+      return 0;
+    }
+}
+
+static void
+spu_value_to_regcache (struct regcache *regcache, int regnum,
+                      struct type *type, const gdb_byte *in)
+{
+  int len = TYPE_LENGTH (type);
+
+  if (spu_scalar_value_p (type))
+    {
+      int preferred_slot = len < 4 ? 4 - len : 0;
+      regcache_cooked_write_part (regcache, regnum, preferred_slot, len, in);
+    }
+  else
+    {
+      while (len >= 16)
+       {
+         regcache_cooked_write (regcache, regnum++, in);
+         in += 16;
+         len -= 16;
+       }
+
+      if (len > 0)
+       regcache_cooked_write_part (regcache, regnum, 0, len, in);
+    }
+}
+
+static void
+spu_regcache_to_value (struct regcache *regcache, int regnum,
+                      struct type *type, gdb_byte *out)
+{
+  int len = TYPE_LENGTH (type);
+
+  if (spu_scalar_value_p (type))
+    {
+      int preferred_slot = len < 4 ? 4 - len : 0;
+      regcache_cooked_read_part (regcache, regnum, preferred_slot, len, out);
+    }
+  else
+    {
+      while (len >= 16)
+       {
+         regcache_cooked_read (regcache, regnum++, out);
+         out += 16;
+         len -= 16;
+       }
+
+      if (len > 0)
+       regcache_cooked_read_part (regcache, regnum, 0, len, out);
+    }
+}
+
+static CORE_ADDR
+spu_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
+                    struct regcache *regcache, CORE_ADDR bp_addr,
+                    int nargs, struct value **args, CORE_ADDR sp,
+                    int struct_return, CORE_ADDR struct_addr)
+{
+  int i;
+  int regnum = SPU_ARG1_REGNUM;
+  int stack_arg = -1;
+  gdb_byte buf[16];
+
+  /* Set the return address.  */
+  memset (buf, 0, sizeof buf);
+  store_unsigned_integer (buf, 4, bp_addr);
+  regcache_cooked_write (regcache, SPU_LR_REGNUM, buf);
+
+  /* If STRUCT_RETURN is true, then the struct return address (in
+     STRUCT_ADDR) will consume the first argument-passing register.
+     Both adjust the register count and store that value.  */
+  if (struct_return)
+    {
+      memset (buf, 0, sizeof buf);
+      store_unsigned_integer (buf, 4, struct_addr);
+      regcache_cooked_write (regcache, regnum++, buf);
+    }
+
+  /* Fill in argument registers.  */
+  for (i = 0; i < nargs; i++)
+    {
+      struct value *arg = args[i];
+      struct type *type = check_typedef (value_type (arg));
+      const gdb_byte *contents = value_contents (arg);
+      int len = TYPE_LENGTH (type);
+      int n_regs = align_up (len, 16) / 16;
+
+      /* If the argument doesn't wholly fit into registers, it and
+        all subsequent arguments go to the stack.  */
+      if (regnum + n_regs - 1 > SPU_ARGN_REGNUM)
+       {
+         stack_arg = i;
+         break;
+       }
+
+      spu_value_to_regcache (regcache, regnum, type, contents);
+      regnum += n_regs;
+    }
+
+  /* Overflow arguments go to the stack.  */
+  if (stack_arg != -1)
+    {
+      CORE_ADDR ap;
+
+      /* Allocate all required stack size.  */
+      for (i = stack_arg; i < nargs; i++)
+       {
+         struct type *type = check_typedef (value_type (args[i]));
+         sp -= align_up (TYPE_LENGTH (type), 16);
+       }
+
+      /* Fill in stack arguments.  */
+      ap = sp;
+      for (i = stack_arg; i < nargs; i++)
+       {
+         struct value *arg = args[i];
+         struct type *type = check_typedef (value_type (arg));
+         int len = TYPE_LENGTH (type);
+         int preferred_slot;
+         
+         if (spu_scalar_value_p (type))
+           preferred_slot = len < 4 ? 4 - len : 0;
+         else
+           preferred_slot = 0;
+
+         target_write_memory (ap + preferred_slot, value_contents (arg), len);
+         ap += align_up (TYPE_LENGTH (type), 16);
+       }
+    }
+
+  /* Allocate stack frame header.  */
+  sp -= 32;
+
+  /* Finally, update the SP register.  */
+  regcache_cooked_write_unsigned (regcache, SPU_SP_REGNUM, sp);
+
+  return sp;
+}
+
+static struct frame_id
+spu_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_id_build (spu_unwind_sp (gdbarch, next_frame),
+                        spu_unwind_pc (gdbarch, next_frame));
+}
+
+/* Function return value access.  */
+
+static enum return_value_convention
+spu_return_value (struct gdbarch *gdbarch, struct type *type,
+                  struct regcache *regcache, gdb_byte *out, const gdb_byte *in)
+{
+  enum return_value_convention rvc;
+
+  if (TYPE_LENGTH (type) <= (SPU_ARGN_REGNUM - SPU_ARG1_REGNUM + 1) * 16)
+    rvc = RETURN_VALUE_REGISTER_CONVENTION;
+  else
+    rvc = RETURN_VALUE_STRUCT_CONVENTION;
+
+  if (in)
+    {
+      switch (rvc)
+       {
+       case RETURN_VALUE_REGISTER_CONVENTION:
+         spu_value_to_regcache (regcache, SPU_ARG1_REGNUM, type, in);
+         break;
+
+       case RETURN_VALUE_STRUCT_CONVENTION:
+         error ("Cannot set function return value.");
+         break;
+       }
+    }
+  else if (out)
+    {
+      switch (rvc)
+       {
+       case RETURN_VALUE_REGISTER_CONVENTION:
+         spu_regcache_to_value (regcache, SPU_ARG1_REGNUM, type, out);
+         break;
+
+       case RETURN_VALUE_STRUCT_CONVENTION:
+         error ("Function return value unknown.");
+         break;
+       }
+    }
+
+  return rvc;
+}
+
+
+/* Breakpoints.  */
+
+static const gdb_byte *
+spu_breakpoint_from_pc (CORE_ADDR * pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x00, 0x00, 0x3f, 0xff };
+
+  *lenptr = sizeof breakpoint;
+  return breakpoint;
+}
+
+
+/* Software single-stepping support.  */
+
+void
+spu_software_single_step (enum target_signal signal, int insert_breakpoints_p)
+{
+  if (insert_breakpoints_p)
+    {
+      CORE_ADDR pc, next_pc;
+      unsigned int insn;
+      int offset, reg;
+      gdb_byte buf[4];
+
+      regcache_cooked_read (current_regcache, SPU_PC_REGNUM, buf);
+      pc = extract_unsigned_integer (buf, 4);
+
+      if (target_read_memory (pc, buf, 4))
+       return;
+      insn = extract_unsigned_integer (buf, 4);
+
+       /* Next sequential instruction is at PC + 4, except if the current
+         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) & -4;
+      else
+       next_pc = (pc + 4) & (SPU_LS_SIZE - 1) & -4;
+
+      insert_single_step_breakpoint (next_pc);
+
+      if (is_branch (insn, &offset, &reg))
+       {
+         CORE_ADDR target = offset;
+
+         if (reg == SPU_PC_REGNUM)
+           target += pc;
+         else if (reg != -1)
+           {
+             regcache_cooked_read_part (current_regcache, reg, 0, 4, buf);
+             target += extract_unsigned_integer (buf, 4);
+           }
+
+         target = target & (SPU_LS_SIZE - 1) & -4;
+         if (target != next_pc)
+           insert_single_step_breakpoint (target);
+       }
+    }
+  else
+    remove_single_step_breakpoints ();
+}
+
+
+/* Set up gdbarch struct.  */
+
+static struct gdbarch *
+spu_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+
+  /* Find a candidate among the list of pre-declared architectures.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Is is for us?  */
+  if (info.bfd_arch_info->mach != bfd_mach_spu)
+    return NULL;
+
+  /* Yes, create a new architecture.  */
+  gdbarch = gdbarch_alloc (&info, NULL);
+
+  /* Disassembler.  */
+  set_gdbarch_print_insn (gdbarch, print_insn_spu);
+
+  /* Registers.  */
+  set_gdbarch_num_regs (gdbarch, SPU_NUM_REGS);
+  set_gdbarch_num_pseudo_regs (gdbarch, SPU_NUM_PSEUDO_REGS);
+  set_gdbarch_sp_regnum (gdbarch, SPU_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, SPU_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, spu_register_name);
+  set_gdbarch_register_type (gdbarch, spu_register_type);
+  set_gdbarch_pseudo_register_read (gdbarch, spu_pseudo_register_read);
+  set_gdbarch_pseudo_register_write (gdbarch, spu_pseudo_register_write);
+  set_gdbarch_convert_register_p (gdbarch, spu_convert_register_p);
+  set_gdbarch_register_to_value (gdbarch, spu_register_to_value);
+  set_gdbarch_value_to_register (gdbarch, spu_value_to_register);
+  set_gdbarch_register_reggroup_p (gdbarch, spu_register_reggroup_p);
+
+  /* Data types.  */
+  set_gdbarch_char_signed (gdbarch, 0);
+  set_gdbarch_ptr_bit (gdbarch, 32);
+  set_gdbarch_addr_bit (gdbarch, 32);
+  set_gdbarch_short_bit (gdbarch, 16);
+  set_gdbarch_int_bit (gdbarch, 32);
+  set_gdbarch_long_bit (gdbarch, 32);
+  set_gdbarch_long_long_bit (gdbarch, 64);
+  set_gdbarch_float_bit (gdbarch, 32);
+  set_gdbarch_double_bit (gdbarch, 64);
+  set_gdbarch_long_double_bit (gdbarch, 64);
+  set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_big);
+  set_gdbarch_double_format (gdbarch, &floatformat_ieee_double_big);
+  set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_double_big);
+
+  /* Inferior function calls.  */
+  set_gdbarch_push_dummy_call (gdbarch, spu_push_dummy_call);
+  set_gdbarch_unwind_dummy_id (gdbarch, spu_unwind_dummy_id);
+  set_gdbarch_return_value (gdbarch, spu_return_value);
+
+  /* Frame handling.  */
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  frame_unwind_append_sniffer (gdbarch, spu_frame_sniffer);
+  frame_base_set_default (gdbarch, &spu_frame_base);
+  set_gdbarch_unwind_pc (gdbarch, spu_unwind_pc);
+  set_gdbarch_unwind_sp (gdbarch, spu_unwind_sp);
+  set_gdbarch_virtual_frame_pointer (gdbarch, spu_virtual_frame_pointer);
+  set_gdbarch_frame_args_skip (gdbarch, 0);
+  set_gdbarch_skip_prologue (gdbarch, spu_skip_prologue);
+
+  /* Breakpoints.  */
+  set_gdbarch_decr_pc_after_break (gdbarch, 4);
+  set_gdbarch_breakpoint_from_pc (gdbarch, spu_breakpoint_from_pc);
+  set_gdbarch_cannot_step_breakpoint (gdbarch, 1);
+  set_gdbarch_software_single_step (gdbarch, spu_software_single_step);
+
+  return gdbarch;
+}
+
+void
+_initialize_spu_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_spu, spu_gdbarch_init);
+}
diff --git a/gdb/spu-tdep.h b/gdb/spu-tdep.h
new file mode 100644 (file)
index 0000000..8bf8309
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPU target-dependent code for GDB, the GNU debugger.
+   Copyright (C) 2006 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 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., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#ifndef SPU_TDEP_H
+#define SPU_TDEP_H
+
+/* Number of registers.  */
+#define SPU_NUM_REGS         130
+#define SPU_NUM_PSEUDO_REGS  1
+#define SPU_NUM_GPRS        128
+
+/* Register numbers of various important registers.  */
+enum spu_regnum
+{
+  /* SPU calling convention.  */
+  SPU_LR_REGNUM = 0,           /* Link register.  */
+  SPU_RAW_SP_REGNUM = 1,       /* Stack pointer (full register).  */
+  SPU_ARG1_REGNUM = 3,         /* First argument register.  */
+  SPU_ARGN_REGNUM = 74,                /* Last argument register.  */
+  SPU_SAVED1_REGNUM = 80,      /* First call-saved register.  */
+  SPU_SAVEDN_REGNUM = 127,     /* Last call-saved register.  */
+  SPU_FP_REGNUM = 127,         /* Frame pointer.  */
+
+  /* Special registers.  */
+  SPU_ID_REGNUM = 128,         /* SPU ID register.  */
+  SPU_PC_REGNUM = 129,         /* Next program counter.  */
+  SPU_SP_REGNUM = 130          /* Stack pointer (preferred slot).  */
+};
+
+/* Local store.  */
+#define SPU_LS_SIZE          0x40000
+
+#endif