* [RFA][3/5] New port: Cell BE SPU (the port itself)
@ 2006-11-11 18:39 Ulrich Weigand
2006-11-11 21:19 ` Mark Kettenis
0 siblings, 1 reply; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-11 18:39 UTC (permalink / raw)
To: gdb-patches
Hello,
this is the bulk of the new SPU port. It adds a configuration for
spu*-*-* as target, as well as a 'pseudo-native' configuration hosted
on powerpc that allows debugging SPU code natively on a Cell BE system.
OK?
Bye,
Ulrich
ChangeLog:
* config/spu/spu-cell.mt: New file.
* config/spu/spu.mt: New file.
* configure.tgt: Add "spu" target_cpu and "spu*-*-*" target.
* Makefile.in (spu_tdep_h): New variable.
(ALLDEPFILES): Add spu-nat.c and spu-tdep.c
(spu-nat.o, spu-tdep.o): Add dependencies.
* spu-nat.c: New file.
* spu-tdep.c: New file.
* spu-tdep.h: New file.
diff -urN gdb-orig/gdb/config/spu/spu-cell.mt gdb-head/gdb/config/spu/spu-cell.mt
--- gdb-orig/gdb/config/spu/spu-cell.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu-cell.mt 2006-11-10 02:10:48.495877384 +0100
@@ -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.
+
+TDEPFILES = spu-tdep.o spu-nat.o fork-child.o inf-ptrace.o
+
diff -urN gdb-orig/gdb/config/spu/spu.mt gdb-head/gdb/config/spu/spu.mt
--- gdb-orig/gdb/config/spu/spu.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu.mt 2006-11-10 02:10:48.497877080 +0100
@@ -0,0 +1,2 @@
+# Target: Cell BE SPU
+TDEPFILES= spu-tdep.o
diff -urN gdb-orig/gdb/configure.tgt gdb-head/gdb/configure.tgt
--- gdb-orig/gdb/configure.tgt 2006-10-31 14:15:40.000000000 +0100
+++ gdb-head/gdb/configure.tgt 2006-11-10 02:10:48.501876472 +0100
@@ -25,6 +25,7 @@
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 ;;
@@ -208,6 +209,13 @@
sparc-*-*) gdb_target=sparc ;;
sparc64-*-*) gdb_target=sparc64 ;;
+spu*-*-*) gdb_target=spu
+ # Support 'pseudo-native' debugging on the Cell BE
+ if test "${gdb_host_cpu}" = "powerpc"; then
+ gdb_target=spu-cell
+ fi
+ ;;
+
xstormy16-*-*) gdb_target=xstormy16 ;;
v850*-*-elf) gdb_target=v850 ;;
diff -urN gdb-orig/gdb/Makefile.in gdb-head/gdb/Makefile.in
--- gdb-orig/gdb/Makefile.in 2006-10-31 14:15:40.000000000 +0100
+++ gdb-head/gdb/Makefile.in 2006-11-10 02:10:48.512874800 +0100
@@ -799,6 +799,7 @@
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
@@ -1496,6 +1497,7 @@
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-nat.c spu-tdep.c \
v850-tdep.c \
vax-nat.c vax-tdep.c vaxbsd-nat.c vaxnbsd-tdep.c \
win32-nat.c \
@@ -2718,6 +2720,15 @@
$(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-nat.o: spu-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 -urN gdb-orig/gdb/spu-nat.c gdb-head/gdb/spu-nat.c
--- gdb-orig/gdb/spu-nat.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-nat.c 2006-11-10 02:12:32.051845768 +0100
@@ -0,0 +1,563 @@
+/* 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"
+
+/* Some older glibc versions do not define this. */
+#ifndef __WNOTHREAD
+#define __WNOTHREAD 0x20000000 /* Don't wait on children of other
+ threads in this group */
+#endif
+
+/* 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. */
+ {
+ char 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];
+ sprintf (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, char *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, char *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)
+{
+ char 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;
+
+ sprintf (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. */
+ sprintf (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_CORE_REGS))
+ {
+ gdb_byte buf[16*SPU_NUM_CORE_REGS];
+ char annex[32];
+ int i;
+
+ sprintf (annex, "%d/regs", fd);
+ if (spu_proc_xfer_spu (annex, buf, NULL, 0, sizeof buf) == sizeof buf)
+ for (i = 0; i < SPU_NUM_CORE_REGS; 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)
+ {
+ char 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_CORE_REGS))
+ {
+ gdb_byte buf[16*SPU_NUM_CORE_REGS];
+ char annex[32];
+ int i;
+
+ for (i = 0; i < SPU_NUM_CORE_REGS; i++)
+ regcache_raw_collect (current_regcache, i, buf + i*16);
+
+ sprintf (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. */
+ sprintf (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 -urN gdb-orig/gdb/spu-tdep.c gdb-head/gdb/spu-tdep.c
--- gdb-orig/gdb/spu-tdep.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.c 2006-11-10 02:10:48.569866136 +0100
@@ -0,0 +1,1098 @@
+/* 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_CORE_REGS)
+ 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)
+{
+ char 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)
+{
+ char 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_CORE_REGS && 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_CORE_REGS];
+ };
+
+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_CORE_REGS];
+ char 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_CORE_REGS; 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_CORE_REGS; 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;
+ char 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_CORE_REGS; i++)
+ if (i == SPU_LR_REGNUM || i > SPU_ARGN_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 char *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, char *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;
+ char 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 char *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 unsigned char *
+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;
+ char 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, ®))
+ {
+ 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 -urN gdb-orig/gdb/spu-tdep.h gdb-head/gdb/spu-tdep.h
--- gdb-orig/gdb/spu-tdep.h 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.h 2006-11-10 02:10:48.572865680 +0100
@@ -0,0 +1,44 @@
+/* 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_CORE_REGS 128
+
+/* SPU calling convention. */
+#define SPU_LR_REGNUM 0
+#define SPU_RAW_SP_REGNUM 1
+#define SPU_ARG1_REGNUM 3
+#define SPU_ARGN_REGNUM 79
+#define SPU_FP_REGNUM 127
+
+/* Special registers. */
+#define SPU_ID_REGNUM 128
+#define SPU_PC_REGNUM 129
+#define SPU_SP_REGNUM 130
+
+/* Local store. */
+#define SPU_LS_SIZE 0x40000
+
+#endif
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-11 18:39 [RFA][3/5] New port: Cell BE SPU (the port itself) Ulrich Weigand
@ 2006-11-11 21:19 ` Mark Kettenis
2006-11-12 15:38 ` Ulrich Weigand
0 siblings, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2006-11-11 21:19 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: gdb-patches
> Hello,
>
> this is the bulk of the new SPU port. It adds a configuration for
> spu*-*-* as target, as well as a 'pseudo-native' configuration hosted
> on powerpc that allows debugging SPU code natively on a Cell BE system.
>
> OK?
Hi Ulrich,
A couple of points:
First, "spu" doesn't occur anywhere in config.guess. Is this a name the
community agrees on? I understand it stands for Synergistic Processor Unit,
and it seems a bad idea to me that's a fairly generic term.
> * config/spu/spu-cell.mt: New file.
> * config/spu/spu.mt: New file.
Two .mt files? I think spu-cell.mt should be renamed spu.mh.
> * spu-nat.c: New file.
This file seems to be Linux-spefic. Can you rename it to spu-linux-nat.c?
> +/* Some older glibc versions do not define this. */
> +#ifndef __WNOTHREAD
> +#define __WNOTHREAD 0x20000000 /* Don't wait on children of
> other
> + threads in this group */
> +#endif
Is this really needed? I mean, sometimes stuff like this gets added when
we're working on getting anew platform running, but things get fixed
before there's an "offical" release.
> + sprintf (mess, "reading PPC register #%d", regno);
Can you use xsnprintf() wherever you use sprintf? The later is a very
dangerous function, and really should not be used in new code.
> + char buf[8];
Lots of places use char where you should use gdb_byte.
> diff -urN gdb-orig/gdb/spu-tdep.h gdb-head/gdb/spu-tdep.h
> --- gdb-orig/gdb/spu-tdep.h 1970-01-01 01:00:00.000000000 +0100
> +++ gdb-head/gdb/spu-tdep.h 2006-11-10 02:10:48.572865680 +0100
> @@ -0,0 +1,44 @@
> +/* 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_CORE_REGS 128
> +
> +/* SPU calling convention. */
> +#define SPU_LR_REGNUM 0
> +#define SPU_RAW_SP_REGNUM 1
> +#define SPU_ARG1_REGNUM 3
> +#define SPU_ARGN_REGNUM 79
> +#define SPU_FP_REGNUM 127
> +
> +/* Special registers. */
> +#define SPU_ID_REGNUM 128
> +#define SPU_PC_REGNUM 129
> +#define SPU_SP_REGNUM 130
> +
> +/* Local store. */
> +#define SPU_LS_SIZE 0x40000
> +
> +#endif
Many ports use an enum for the register numbers, which makes debugging a
bit easier. I also think SPU_NUM_CORE_REGS is a bad name. I first thought
this had something to do with core files. May I suggest SPU_NUM_VEC_REGS.
Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-11 21:19 ` Mark Kettenis
@ 2006-11-12 15:38 ` Ulrich Weigand
2006-11-12 21:42 ` Mark Kettenis
0 siblings, 1 reply; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-12 15:38 UTC (permalink / raw)
To: Mark Kettenis; +Cc: gdb-patches
Hi Mark,
thanks for looking at the port!
> First, "spu" doesn't occur anywhere in config.guess. Is this a name the
> community agrees on? I understand it stands for Synergistic Processor Unit,
> and it seems a bad idea to me that's a fairly generic term.
"spu" cannot occur as host architecture, therefore it is not in config.guess.
It is, however, in config.sub as a target architecture, and the recently
committed binutils patch uses it (as does the proposed GCC patch and all the
previously released toolchain packages for Cell, both for Linux and the PS3).
> > * config/spu/spu-cell.mt: New file.
> > * config/spu/spu.mt: New file.
>
> Two .mt files? I think spu-cell.mt should be renamed spu.mh.
Since spu is never the host architecture, spu.mh would not get used.
The situation is a bit unique here: The spu debugger is itself a PowerPC
binary and runs on the PowerPC side of the Cell, but it debugs code running
on the SPU side, so it would be a "cross" configuration. On the other hand,
the debugger makes use of special host operating system facilities (ptrace
plus the spufs file system) to control the SPU inferior -- in this aspect
it looks like a "native" configuration.
I've tried different ways to integrate this scenario into the GDB configure
structure, and what I've come up with appeared to me to be the most straight-
forward way.
This means I leave GDB's notion of "host" as auto-detected (i.e. powerpc64),
which means that GDB does not configure as a "native" target (since target
!= host). However, the target-dependent files for the spu target actually
include the spu-linux-nat.c file which installs itself onto the target stack
and provides the "native" debugging capabilities that way.
Of course I also need the "real" cross-SPU target, e.g. to build an x86-
hosted spu cross-debugger that can attach to a gdbserver on the Cell.
To achieve this I've defined *two* target definitions, and have configure.tgt
select which of those to use depending on the host architecture.
> > * spu-nat.c: New file.
>
> This file seems to be Linux-spefic. Can you rename it to spu-linux-nat.c?
Sure, done.
> > +/* Some older glibc versions do not define this. */
> > +#ifndef __WNOTHREAD
> > +#define __WNOTHREAD 0x20000000 /* Don't wait on children of
> > other
> > + threads in this group */
> > +#endif
>
> Is this really needed? I mean, sometimes stuff like this gets added when
> we're working on getting anew platform running, but things get fixed
> before there's an "offical" release.
Since this define was added only with glibc 2.4, we've really needed this
until quite recently. However, I'm fine with removing it from the version
in the FSF repository, this should be used with current/future distributions
only, anyway.
> > + sprintf (mess, "reading PPC register #%d", regno);
>
> Can you use xsnprintf() wherever you use sprintf? The later is a very
> dangerous function, and really should not be used in new code.
Done.
> > + char buf[8];
>
> Lots of places use char where you should use gdb_byte.
I thought I went through that already, but I did indeed miss some. I hope
I've now caught them all ...
> Many ports use an enum for the register numbers, which makes debugging a
> bit easier.
Done. Doing so I noticed that I forgot to implement the latest ABI
change reserving registers 75-79 as scratch registers for call stubs.
I've fixed this now as well.
> I also think SPU_NUM_CORE_REGS is a bad name. I first thought
> this had something to do with core files.
Agreed, I've renamed it.
> May I suggest SPU_NUM_VEC_REGS.
Since the SPU ISA calls them "general-purpose registers" (and it's a bit
less to type), I'm now using SPU_NUM_GPRS. OK?
Here's the current version of the patch including the above-mentioned
changes. Together with the other four patches I'm still getting the
same testsuite results.
Bye,
Ulrich
ChangeLog:
* config/spu/spu-cell.mt: New file.
* config/spu/spu.mt: New file.
* 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.
diff -urN gdb-orig/gdb/config/spu/spu-cell.mt gdb-head/gdb/config/spu/spu-cell.mt
--- gdb-orig/gdb/config/spu/spu-cell.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu-cell.mt 2006-11-12 15:04:35.110937504 +0100
@@ -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.
+
+TDEPFILES = spu-tdep.o spu-linux-nat.o fork-child.o inf-ptrace.o
+
diff -urN gdb-orig/gdb/config/spu/spu.mt gdb-head/gdb/config/spu/spu.mt
--- gdb-orig/gdb/config/spu/spu.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu.mt 2006-11-12 14:57:46.712974288 +0100
@@ -0,0 +1,2 @@
+# Target: Cell BE SPU
+TDEPFILES= spu-tdep.o
diff -urN gdb-orig/gdb/configure.tgt gdb-head/gdb/configure.tgt
--- gdb-orig/gdb/configure.tgt 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/configure.tgt 2006-11-12 16:09:29.132964800 +0100
@@ -25,6 +25,7 @@
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 ;;
@@ -208,6 +209,13 @@
sparc-*-*) gdb_target=sparc ;;
sparc64-*-*) gdb_target=sparc64 ;;
+spu*-*-*) gdb_target=spu
+ # Support 'pseudo-native' debugging on the Cell BE
+ if test "${gdb_host_cpu}" = "powerpc"; then
+ gdb_target=spu-cell
+ fi
+ ;;
+
xstormy16-*-*) gdb_target=xstormy16 ;;
v850*-*-elf) gdb_target=v850 ;;
diff -urN gdb-orig/gdb/Makefile.in gdb-head/gdb/Makefile.in
--- gdb-orig/gdb/Makefile.in 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/Makefile.in 2006-11-12 15:04:08.590974744 +0100
@@ -799,6 +799,7 @@
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
@@ -1496,6 +1497,7 @@
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 \
@@ -2718,6 +2720,15 @@
$(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 -urN gdb-orig/gdb/spu-linux-nat.c gdb-head/gdb/spu-linux-nat.c
--- gdb-orig/gdb/spu-linux-nat.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-linux-nat.c 2006-11-12 15:54:26.112962216 +0100
@@ -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 -urN gdb-orig/gdb/spu-tdep.c gdb-head/gdb/spu-tdep.c
--- gdb-orig/gdb/spu-tdep.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.c 2006-11-12 15:47:38.159934552 +0100
@@ -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, ®))
+ {
+ 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 -urN gdb-orig/gdb/spu-tdep.h gdb-head/gdb/spu-tdep.h
--- gdb-orig/gdb/spu-tdep.h 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.h 2006-11-12 15:58:09.260991552 +0100
@@ -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
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-12 15:38 ` Ulrich Weigand
@ 2006-11-12 21:42 ` Mark Kettenis
2006-11-12 22:12 ` Daniel Jacobowitz
0 siblings, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2006-11-12 21:42 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Mark Kettenis, gdb-patches
> Hi Mark,
>
> thanks for looking at the port!
>
> > First, "spu" doesn't occur anywhere in config.guess. Is this a name the
> > community agrees on? I understand it stands for Synergistic Processor
> > Unit,
> > and it seems a bad idea to me that's a fairly generic term.
>
> "spu" cannot occur as host architecture, therefore it is not in
> config.guess.
> It is, however, in config.sub as a target architecture, and the recently
> committed binutils patch uses it (as does the proposed GCC patch and all
> the
> previously released toolchain packages for Cell, both for Linux and the
> PS3).
Ok,
>
> > > * config/spu/spu-cell.mt: New file.
> > > * config/spu/spu.mt: New file.
> >
> > Two .mt files? I think spu-cell.mt should be renamed spu.mh.
>
> Since spu is never the host architecture, spu.mh would not get used.
>
> The situation is a bit unique here: The spu debugger is itself a PowerPC
> binary and runs on the PowerPC side of the Cell, but it debugs code
> running
> on the SPU side, so it would be a "cross" configuration. On the other
> hand,
> the debugger makes use of special host operating system facilities (ptrace
> plus the spufs file system) to control the SPU inferior -- in this aspect
> it looks like a "native" configuration.
I think *is* a native configuration.
>
> I've tried different ways to integrate this scenario into the GDB
> configure
> structure, and what I've come up with appeared to me to be the most
> straight-
> forward way.
I think it is fundamentally wrong to put native-dependent code in a .mt
file.
> This means I leave GDB's notion of "host" as auto-detected (i.e.
> powerpc64),
> which means that GDB does not configure as a "native" target (since target
> != host). However, the target-dependent files for the spu target actually
> include the spu-linux-nat.c file which installs itself onto the target
> stack
> and provides the "native" debugging capabilities that way.
I think that what you really want is a Linux powerpc native configuration
that can debug both normal powerpc code and spu code. That'd mean adding
spu-linux-nat.c to config/powerpc/linux.mh. But I suppose that doesn't
really work right now. But could we make that work?
> > I also think SPU_NUM_CORE_REGS is a bad name. I first thought
> > this had something to do with core files.
>
> Agreed, I've renamed it.
>
> > May I suggest SPU_NUM_VEC_REGS.
>
> Since the SPU ISA calls them "general-purpose registers" (and it's a bit
> less to type), I'm now using SPU_NUM_GPRS. OK?
Personally I'd prefer something that follows the SPU_NUM_XXX_REGS pattern,
but that's not terribly important.
Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-12 21:42 ` Mark Kettenis
@ 2006-11-12 22:12 ` Daniel Jacobowitz
2006-11-13 12:27 ` Ulrich Weigand
0 siblings, 1 reply; 20+ messages in thread
From: Daniel Jacobowitz @ 2006-11-12 22:12 UTC (permalink / raw)
To: Mark Kettenis; +Cc: Ulrich Weigand, gdb-patches
On Sun, Nov 12, 2006 at 10:41:34PM +0100, Mark Kettenis wrote:
>
> > This means I leave GDB's notion of "host" as auto-detected (i.e.
> > powerpc64),
> > which means that GDB does not configure as a "native" target (since target
> > != host). However, the target-dependent files for the spu target actually
> > include the spu-linux-nat.c file which installs itself onto the target
> > stack
> > and provides the "native" debugging capabilities that way.
>
> I think that what you really want is a Linux powerpc native configuration
> that can debug both normal powerpc code and spu code. That'd mean adding
> spu-linux-nat.c to config/powerpc/linux.mh. But I suppose that doesn't
> really work right now. But could we make that work?
In theory yes - but I'm not quite sure how. You'd have more than one
target that could take control when you said "run" and for Cell I think
you'd have to disambiguate based on the architecture of the file. But
Ulrich said they had more patches that weren't ready for mainline and I
bet some of them make this nicer :-) Since really you would want to
debug both at once.
In the mean time, I suppose you could configure a native powerpc64
debugger with some special flag that caused it to only work for SPU
instead of PPC64, but if I had to come up with a way to do this, I'm
afraid I'd end up with exactly what Ulrich did: a ppc-linux->spu-elf
debugger that knew how to run things on the SPU.
I guess what really is throwing us here is the use of "nat". Isn't
this really more like one of the custom remote-foo.c targets than a
native target? It just happens to be implemented using PowerPC/Linux
kernel facilities spelled "ptrace" and some poking around in a PowerPC
executable in order to implement "run". The ptrace facilities don't
seem to be used much to talk to the SPU; new files in /proc are used
instead. It's forking and running a PowerPC executable until it makes
a special SPU-related syscall, and then it starts talking to the SPU.
That's an oversimplification; this is quite twisty!
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-12 22:12 ` Daniel Jacobowitz
@ 2006-11-13 12:27 ` Ulrich Weigand
2006-11-13 12:43 ` Mark Kettenis
0 siblings, 1 reply; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-13 12:27 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Mark Kettenis, gdb-patches
Daniel Jacobowitz wrote:
> On Sun, Nov 12, 2006 at 10:41:34PM +0100, Mark Kettenis wrote:
> > I think that what you really want is a Linux powerpc native configuration
> > that can debug both normal powerpc code and spu code. That'd mean adding
> > spu-linux-nat.c to config/powerpc/linux.mh. But I suppose that doesn't
> > really work right now. But could we make that work?
>
> In theory yes - but I'm not quite sure how. You'd have more than one
> target that could take control when you said "run" and for Cell I think
> you'd have to disambiguate based on the architecture of the file. But
> Ulrich said they had more patches that weren't ready for mainline and I
> bet some of them make this nicer :-) Since really you would want to
> debug both at once.
Yes, exactly. It's not just a matter of checking the executable file
architecture; a single process can have threads executing SPU code at
the same as other threads executing PowerPC code.
I have a set of patches that does appear to work so far; it is based
primarily on switching current_gdbarch on thread switch. However,
there's still some work to be done before this is in a shape suitable
for mainline inclusion.
Therefore I'd hoped it would be possible to get the SPU-only port
accepted first, since this is in itself already quite useful.
> I guess what really is throwing us here is the use of "nat". Isn't
> this really more like one of the custom remote-foo.c targets than a
> native target? It just happens to be implemented using PowerPC/Linux
> kernel facilities spelled "ptrace" and some poking around in a PowerPC
> executable in order to implement "run". The ptrace facilities don't
> seem to be used much to talk to the SPU; new files in /proc are used
> instead. It's forking and running a PowerPC executable until it makes
> a special SPU-related syscall, and then it starts talking to the SPU.
I'll have a look at the remote-foo targets. Is it just more or less just
a matter of renaming the file, or are there significant differences?
Bye,
Ulrich
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-13 12:27 ` Ulrich Weigand
@ 2006-11-13 12:43 ` Mark Kettenis
2006-11-13 13:48 ` Daniel Jacobowitz
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Mark Kettenis @ 2006-11-13 12:43 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Daniel Jacobowitz, Mark Kettenis, gdb-patches
> Daniel Jacobowitz wrote:
>
> > On Sun, Nov 12, 2006 at 10:41:34PM +0100, Mark Kettenis wrote:
> > > I think that what you really want is a Linux powerpc native
> > configuration
> > > that can debug both normal powerpc code and spu code. That'd mean
> > adding
> > > spu-linux-nat.c to config/powerpc/linux.mh. But I suppose that
> > doesn't
> > > really work right now. But could we make that work?
> >
> > In theory yes - but I'm not quite sure how. You'd have more than one
> > target that could take control when you said "run" and for Cell I think
> > you'd have to disambiguate based on the architecture of the file. But
> > Ulrich said they had more patches that weren't ready for mainline and I
> > bet some of them make this nicer :-) Since really you would want to
> > debug both at once.
>
> Yes, exactly. It's not just a matter of checking the executable file
> architecture; a single process can have threads executing SPU code at
> the same as other threads executing PowerPC code.
That's not really what I'm talking about. I'm talking about having a single
gdb binary that is able to execute both native powerpc code and spu code
(but not at the same time). As far as I can tell, it should be possible
to do that by implementing to_can_run() in both ppc-linux-nat.c and
spu-linux-nat.c, such that only one returns true at any time.
> I have a set of patches that does appear to work so far; it is based
> primarily on switching current_gdbarch on thread switch. However,
> there's still some work to be done before this is in a shape suitable
> for mainline inclusion.
Andrew Cagney has talked a fair bit about this sort of things in the past.
His idea was that each frame would have a gdbarch. But a gdbarch per
thread probably makes more sense.
> Therefore I'd hoped it would be possible to get the SPU-only port
> accepted first, since this is in itself already quite useful.
> > I guess what really is throwing us here is the use of "nat". Isn't
> > this really more like one of the custom remote-foo.c targets than a
> > native target? It just happens to be implemented using PowerPC/Linux
> > kernel facilities spelled "ptrace" and some poking around in a PowerPC
> > executable in order to implement "run". The ptrace facilities don't
> > seem to be used much to talk to the SPU; new files in /proc are used
> > instead. It's forking and running a PowerPC executable until it makes
> > a special SPU-related syscall, and then it starts talking to the SPU.
>
> I'll have a look at the remote-foo targets. Is it just more or less just
> a matter of renaming the file, or are there significant differences?
I don't agree with Daniel here. The spu-linux-nat.c target vector is
only usable on a powerpc native debugger.
Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-13 12:43 ` Mark Kettenis
@ 2006-11-13 13:48 ` Daniel Jacobowitz
2006-11-13 19:50 ` Jim Blandy
2006-11-18 0:10 ` Ulrich Weigand
2 siblings, 0 replies; 20+ messages in thread
From: Daniel Jacobowitz @ 2006-11-13 13:48 UTC (permalink / raw)
To: Mark Kettenis; +Cc: Ulrich Weigand, gdb-patches
On Mon, Nov 13, 2006 at 01:42:07PM +0100, Mark Kettenis wrote:
> That's not really what I'm talking about. I'm talking about having a single
> gdb binary that is able to execute both native powerpc code and spu code
> (but not at the same time). As far as I can tell, it should be possible
> to do that by implementing to_can_run() in both ppc-linux-nat.c and
> spu-linux-nat.c, such that only one returns true at any time.
Hmm, I guess that would work. Would you include this in all native
ppc64 debuggers?
> I don't agree with Daniel here. The spu-linux-nat.c target vector is
> only usable on a powerpc native debugger.
Fair enough. I think of this as similar to a hypothetical remote-*.c
module which is only usable on Windows, but I agree that it's not a
good analogy.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-13 12:43 ` Mark Kettenis
2006-11-13 13:48 ` Daniel Jacobowitz
@ 2006-11-13 19:50 ` Jim Blandy
2006-11-18 0:10 ` Ulrich Weigand
2 siblings, 0 replies; 20+ messages in thread
From: Jim Blandy @ 2006-11-13 19:50 UTC (permalink / raw)
To: Mark Kettenis; +Cc: Ulrich Weigand, Daniel Jacobowitz, gdb-patches
"Mark Kettenis" <mark.kettenis@xs4all.nl> writes:
>> I have a set of patches that does appear to work so far; it is based
>> primarily on switching current_gdbarch on thread switch. However,
>> there's still some work to be done before this is in a shape suitable
>> for mainline inclusion.
>
> Andrew Cagney has talked a fair bit about this sort of things in the past.
> His idea was that each frame would have a gdbarch. But a gdbarch per
> thread probably makes more sense.
Not to distract from discussion of Uli's patch, but:
I've worked on a processor that would switch between a normal ISA and
a special VLIW ISA on function calls. The bottom bit of the return
address said which mode to return to on the way out. And ARM allows
calls between Thumb and ARM code.
Making the architecture per-frame, though, raises a bunch of odd
questions. If a register is callee-saves, finding its value in some
frame F entails asking F's callee G for the value. But if G is a
different gdbarch from F, then what register numbering does F use to
make the request?
And that's just off the top of my head. I'll bet there's lots more.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-13 12:43 ` Mark Kettenis
2006-11-13 13:48 ` Daniel Jacobowitz
2006-11-13 19:50 ` Jim Blandy
@ 2006-11-18 0:10 ` Ulrich Weigand
2006-11-18 6:03 ` Daniel Jacobowitz
2006-11-18 17:35 ` Mark Kettenis
2 siblings, 2 replies; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-18 0:10 UTC (permalink / raw)
To: Mark Kettenis; +Cc: Daniel Jacobowitz, Mark Kettenis, gdb-patches
Mark Kettenis wrote:
> That's not really what I'm talking about. I'm talking about having a single
> gdb binary that is able to execute both native powerpc code and spu code
> (but not at the same time). As far as I can tell, it should be possible
> to do that by implementing to_can_run() in both ppc-linux-nat.c and
> spu-linux-nat.c, such that only one returns true at any time.
I've tried but failed to make that work. The problem is that both
ppc-linux-nat.c and spu-linux-nat.c are based on inf-ptrace.c, and
it is currently impossible to register two targets resulting from
inf_ptrace_create at the same time, due to the ptrace_ops_hack.
Appended below is the code I'm currently using. It requires building
a native powerpc64 target with --enable-targets=spu (which is necessary
anyway to get spu BFD support, and now also triggers inclusion of
both spu-linux-nat.c on the host side and spu-tdep.c on the target
side).
Even if this would work somehow, some other features of my original
patch appear difficult to implement. For example, how would a gdbserver
server setup look like? Also, with my approach I can attach to a thread
without having to specify an exec file, and GDB will retrieve a possibly
embedded SPU exec file from the inferior -- this also looks difficult.
More generally, your approach of going towards a single debugger for
Cell is of course something I would like to see work in the end -- but
that is something that requires difficult problems to solve first.
I had hoped to start out with a strictly single-target SPU standalone
port first, that would be easily acceptable by avoiding those difficult
issues, and *then* in a second step address the combined Cell debugger.
I would be happy to try out whatever suggestions you might have to
achieve that goal ...
> > I have a set of patches that does appear to work so far; it is based
> > primarily on switching current_gdbarch on thread switch. However,
> > there's still some work to be done before this is in a shape suitable
> > for mainline inclusion.
>
> Andrew Cagney has talked a fair bit about this sort of things in the past.
> His idea was that each frame would have a gdbarch. But a gdbarch per
> thread probably makes more sense.
In the end having a gdbarch per frame would be preferrable, so that
you can model things like: PPU code calls spu_run, which starts SPU
code execution, then a signal arrives and is handled on the PPU side.
However, even my experimental multi-arch debugger currently does not
support this.
Bye,
Ulrich
diff -urN gdb-orig/gdb/config/powerpc/linux-cell.mt gdb-head/gdb/config/powerpc/linux-cell.mt
--- gdb-orig/gdb/config/powerpc/linux-cell.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/powerpc/linux-cell.mt 2006-11-16 04:24:03.000000000 +0100
@@ -0,0 +1,5 @@
+# Target: Linux on Cell BE (PowerPC + SPU)
+TDEPFILES= rs6000-tdep.o ppc-linux-tdep.o ppc-sysv-tdep.o spu-tdep.o \
+ solib.o solib-svr4.o solib-legacy.o corelow.o symfile-mem.o
+DEPRECATED_TM_FILE= tm-linux.h
+
diff -urN gdb-orig/gdb/config/powerpc/ppc64-linux-cell.mh gdb-head/gdb/config/powerpc/ppc64-linux-cell.mh
--- gdb-orig/gdb/config/powerpc/ppc64-linux-cell.mh 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/powerpc/ppc64-linux-cell.mh 2006-11-16 04:25:21.000000000 +0100
@@ -0,0 +1,18 @@
+# Host: Cell BE (PowerPC64 + SPU), running Linux
+
+XM_CLIBS=
+
+NAT_FILE= nm-ppc64-linux.h
+NATDEPFILES= inf-ptrace.o fork-child.o \
+ ppc-linux-nat.o proc-service.o linux-thread-db.o \
+ gcore.o linux-nat.o spu-linux-nat.o linux-fork.o
+
+# The PowerPC has severe limitations on TOC size, and uses them even
+# for non-PIC code. GDB overflows those tables when compiling with
+# -mfull-toc (the default), so we need to ask GCC to use as few TOC
+# entries as possible.
+MH_CFLAGS= -mminimal-toc
+
+# The dynamically loaded libthread_db needs access to symbols in the
+# gdb executable.
+LOADLIBES= -ldl -rdynamic
diff -urN gdb-orig/gdb/configure.host gdb-head/gdb/configure.host
--- gdb-orig/gdb/configure.host 2006-02-10 22:53:51.000000000 +0100
+++ gdb-head/gdb/configure.host 2006-11-16 04:21:41.000000000 +0100
@@ -113,7 +113,18 @@
gdb_host=nbsd ;;
powerpc-*-openbsd*) gdb_host=obsd ;;
-powerpc64-*-linux*) gdb_host=ppc64-linux ;;
+powerpc64-*-linux*) gdb_host=ppc64-linux
+ # If we were built with --enable-targets=spu,
+ # enable combined Cell BE debugging.
+ if test -n "$enable_targets" ; then
+ for targ in `echo $enable_targets | sed 's/,/ /g'`
+ do
+ case "$targ" in
+ all | spu*) gdb_host=ppc64-linux-cell ;;
+ esac
+ done
+ fi
+ ;;
rs6000-*-lynxos*) gdb_host=rs6000lynx ;;
rs6000-*-aix4*) gdb_host=aix4 ;;
diff -urN gdb-orig/gdb/configure.tgt gdb-head/gdb/configure.tgt
--- gdb-orig/gdb/configure.tgt 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/configure.tgt 2006-11-16 04:19:50.000000000 +0100
@@ -161,6 +161,16 @@
build_gdbserver=yes
;;
powerpc64-*-linux*) gdb_target=linux
+ # If we were built with --enable-targets=spu,
+ # enable combined Cell BE debugging.
+ if test -n "$enable_targets" ; then
+ for targ in `echo $enable_targets | sed 's/,/ /g'`
+ do
+ case "$targ" in
+ all | spu*) gdb_target=linux-cell ;;
+ esac
+ done
+ fi
build_gdbserver=yes
;;
powerpc*-*-*) if test -f ../sim/ppc/Makefile; then
diff -urN gdb-orig/gdb/Makefile.in gdb-head/gdb/Makefile.in
--- gdb-orig/gdb/Makefile.in 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/Makefile.in 2006-11-16 04:16:15.000000000 +0100
@@ -799,6 +799,7 @@
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
@@ -1496,6 +1497,7 @@
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 \
@@ -2718,6 +2720,15 @@
$(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 -urN gdb-orig/gdb/ppc-linux-nat.c gdb-head/gdb/ppc-linux-nat.c
--- gdb-orig/gdb/ppc-linux-nat.c 2006-05-05 22:45:03.000000000 +0200
+++ gdb-head/gdb/ppc-linux-nat.c 2006-11-16 04:29:44.000000000 +0100
@@ -1002,6 +1002,14 @@
}
}
+/* Override the to_can_run routine. */
+static int
+ppc_linux_can_run (void)
+{
+ return !exec_bfd || bfd_get_arch (exec_bfd) != bfd_arch_spu;
+}
+
+
void _initialize_ppc_linux_nat (void);
void
@@ -1012,6 +1020,9 @@
/* Fill in the generic GNU/Linux methods. */
t = linux_target ();
+ /* Override to_can_run routine. */
+ t->to_can_run = ppc_linux_can_run;
+
/* Add our register access methods. */
t->to_fetch_registers = ppc_linux_fetch_inferior_registers;
t->to_store_registers = ppc_linux_store_inferior_registers;
diff -urN gdb-orig/gdb/spu-linux-nat.c gdb-head/gdb/spu-linux-nat.c
--- gdb-orig/gdb/spu-linux-nat.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-linux-nat.c 2006-11-16 04:28:43.000000000 +0100
@@ -0,0 +1,565 @@
+/* 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;
+}
+
+/* Override the to_can_run routine. */
+static int
+spu_can_run (void)
+{
+ return exec_bfd && bfd_get_arch (exec_bfd) == bfd_arch_spu;
+}
+
+
+/* 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_can_run = spu_can_run;
+ 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 -urN gdb-orig/gdb/spu-tdep.c gdb-head/gdb/spu-tdep.c
--- gdb-orig/gdb/spu-tdep.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.c 2006-11-16 04:16:15.000000000 +0100
@@ -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, ®))
+ {
+ 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 -urN gdb-orig/gdb/spu-tdep.h gdb-head/gdb/spu-tdep.h
--- gdb-orig/gdb/spu-tdep.h 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.h 2006-11-16 04:16:15.000000000 +0100
@@ -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
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-18 0:10 ` Ulrich Weigand
@ 2006-11-18 6:03 ` Daniel Jacobowitz
2006-11-18 11:10 ` Ulrich Weigand
2006-11-18 17:35 ` Mark Kettenis
1 sibling, 1 reply; 20+ messages in thread
From: Daniel Jacobowitz @ 2006-11-18 6:03 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Mark Kettenis, gdb-patches
On Sat, Nov 18, 2006 at 01:09:59AM +0100, Ulrich Weigand wrote:
> More generally, your approach of going towards a single debugger for
> Cell is of course something I would like to see work in the end -- but
> that is something that requires difficult problems to solve first.
> I had hoped to start out with a strictly single-target SPU standalone
> port first, that would be easily acceptable by avoiding those difficult
> issues, and *then* in a second step address the combined Cell debugger.
>
> I would be happy to try out whatever suggestions you might have to
> achieve that goal ...
Do you need a native GDB for this? That is, would starting with an SPU
cross target plus an SPU-aware gdbserver be useful? For now that would
probably mean a separate gdbserver which only spoke SPU. I'm not sure
if that's possible at all based on your original patch; just asking.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-18 6:03 ` Daniel Jacobowitz
@ 2006-11-18 11:10 ` Ulrich Weigand
2006-11-18 16:41 ` Daniel Jacobowitz
0 siblings, 1 reply; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-18 11:10 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Mark Kettenis, gdb-patches
Daniel Jacobowitz wrote:
> Do you need a native GDB for this? That is, would starting with an SPU
> cross target plus an SPU-aware gdbserver be useful? For now that would
> probably mean a separate gdbserver which only spoke SPU. I'm not sure
> if that's possible at all based on your original patch; just asking.
If you look at patch 4/5 in my original series, that just what you're
proposing here: a gdbserver that speaks only SPU. We've been using
that to support x86 -> spu cross debugging, but it would certainly
work for ppc -> spu cross debugging as well.
However, the patch has the same type of slight abuse of the build system
that Mark was concerned about for the native patch: it has a hack to build
a gdbserver in a "cross" configure situation. Since I can't really build
a native spu configuration, that's the only way to actually get a gdbserver
built I could think of ...
Bye,
Ulrich
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-18 11:10 ` Ulrich Weigand
@ 2006-11-18 16:41 ` Daniel Jacobowitz
0 siblings, 0 replies; 20+ messages in thread
From: Daniel Jacobowitz @ 2006-11-18 16:41 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Mark Kettenis, gdb-patches
On Sat, Nov 18, 2006 at 12:09:58PM +0100, Ulrich Weigand wrote:
> However, the patch has the same type of slight abuse of the build system
> that Mark was concerned about for the native patch: it has a hack to build
> a gdbserver in a "cross" configure situation. Since I can't really build
> a native spu configuration, that's the only way to actually get a gdbserver
> built I could think of ...
Well, you can build it separately. Unlike other subdirectories, the
configure script in gdb/gdbserver/ can be run directly. Is that all
you need?
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-18 0:10 ` Ulrich Weigand
2006-11-18 6:03 ` Daniel Jacobowitz
@ 2006-11-18 17:35 ` Mark Kettenis
2006-11-21 20:22 ` Ulrich Weigand
1 sibling, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2006-11-18 17:35 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Mark Kettenis, Daniel Jacobowitz, gdb-patches
> I've tried but failed to make that work. The problem is that both
> ppc-linux-nat.c and spu-linux-nat.c are based on inf-ptrace.c, and
> it is currently impossible to register two targets resulting from
> inf_ptrace_create at the same time, due to the ptrace_ops_hack.
Duh. Well, I guess we need to fix that then.
> Appended below is the code I'm currently using. It requires building
> a native powerpc64 target with --enable-targets=spu (which is necessary
> anyway to get spu BFD support, and now also triggers inclusion of
> both spu-linux-nat.c on the host side and spu-tdep.c on the target
> side).
Does this work as well as your previous diff? I like this much better
and as far as I am concerned this can go in if you fix one minor nit:
> gdb-head/gdb/config/powerpc/ppc64-linux-cell.mh
> --- gdb-orig/gdb/config/powerpc/ppc64-linux-cell.mh 1970-01-01
> 01:00:00.000000000 +0100
> +++ gdb-head/gdb/config/powerpc/ppc64-linux-cell.mh 2006-11-16
> 04:25:21.000000000 +0100
> @@ -0,0 +1,18 @@
> +# Host: Cell BE (PowerPC64 + SPU), running Linux
> +
> +XM_CLIBS=
Please get rid of this XM_CLIBS setting. The same for linux-cell.mh.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-18 17:35 ` Mark Kettenis
@ 2006-11-21 20:22 ` Ulrich Weigand
2006-11-21 20:40 ` Daniel Jacobowitz
2006-11-21 21:32 ` Mark Kettenis
0 siblings, 2 replies; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-21 20:22 UTC (permalink / raw)
To: Mark Kettenis; +Cc: Mark Kettenis, Daniel Jacobowitz, gdb-patches
Mark Kettenis wrote:
> > I've tried but failed to make that work. The problem is that both
> > ppc-linux-nat.c and spu-linux-nat.c are based on inf-ptrace.c, and
> > it is currently impossible to register two targets resulting from
> > inf_ptrace_create at the same time, due to the ptrace_ops_hack.
>
> Duh. Well, I guess we need to fix that then.
Suggestions? The cleanest way I could think of would be to actually
extend the to_attach, to_detach, to_create_inferior, and to_mourn_inferior
callbacks with an explict struct target_ops * parameter.
However, that change would have extensive effects throughout GDB; many
of the changes are mechanical, but some appear non-trivial, like the
way the various thread stratum targets call back to the "beneath" target
using saved function pointers ...
> > Appended below is the code I'm currently using. It requires building
> > a native powerpc64 target with --enable-targets=spu (which is necessary
> > anyway to get spu BFD support, and now also triggers inclusion of
> > both spu-linux-nat.c on the host side and spu-tdep.c on the target
> > side).
>
> Does this work as well as your previous diff? I like this much better
> and as far as I am concerned this can go in if you fix one minor nit:
Well, unfortunately due to the ptrace_ops_hack problem it doesn't work
at all as-is. Even if we were to fix that, there's still a problem with
handling attaching to a process without an exec file given to set the
architecture from, and possibly build issues with gdbserver (I haven't
tried this yet).
> > gdb-head/gdb/config/powerpc/ppc64-linux-cell.mh
> > --- gdb-orig/gdb/config/powerpc/ppc64-linux-cell.mh 1970-01-01
> > 01:00:00.000000000 +0100
> > +++ gdb-head/gdb/config/powerpc/ppc64-linux-cell.mh 2006-11-16
> > 04:25:21.000000000 +0100
> > @@ -0,0 +1,18 @@
> > +# Host: Cell BE (PowerPC64 + SPU), running Linux
> > +
> > +XM_CLIBS=
>
> Please get rid of this XM_CLIBS setting. The same for linux-cell.mh.
Sure, that was just copied from ppc64-linux.mh, sorry.
I've now also tried out yet another alternative approach, which avoids
the ptrace_ops_hack problem by supporting only the spu target again.
It still cleanly separates out target vs. host files. However, it does
require a change to configure.ac to allow configure.host to specify that
a configuration should be treated as "native" even though host != target.
This patch does work as well as the original one. Would this (or
something along those lines) also be acceptable?
Bye,
Ulrich
ChangeLog:
* 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.
diff -urN gdb-orig/gdb/config/powerpc/spu-linux.mh gdb-head/gdb/config/powerpc/spu-linux.mh
--- gdb-orig/gdb/config/powerpc/spu-linux.mh 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/powerpc/spu-linux.mh 2006-11-21 15:15:40.000000000 +0100
@@ -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 -urN gdb-orig/gdb/config/spu/spu.mt gdb-head/gdb/config/spu/spu.mt
--- gdb-orig/gdb/config/spu/spu.mt 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/config/spu/spu.mt 2006-11-21 15:14:44.862070320 +0100
@@ -0,0 +1,2 @@
+# Target: Cell BE SPU
+TDEPFILES= spu-tdep.o
diff -urN gdb-orig/gdb/configure.ac gdb-head/gdb/configure.ac
--- gdb-orig/gdb/configure.ac 2006-08-08 22:26:23.000000000 +0200
+++ gdb-head/gdb/configure.ac 2006-11-21 15:32:18.000000000 +0100
@@ -81,6 +81,12 @@
# 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 @@
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 @@
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 '
diff -urN gdb-orig/gdb/configure.host gdb-head/gdb/configure.host
--- gdb-orig/gdb/configure.host 2006-02-10 22:53:51.000000000 +0100
+++ gdb-head/gdb/configure.host 2006-11-21 21:02:19.748182784 +0100
@@ -113,7 +113,13 @@
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 ;;
diff -urN gdb-orig/gdb/configure.tgt gdb-head/gdb/configure.tgt
--- gdb-orig/gdb/configure.tgt 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/configure.tgt 2006-11-21 15:16:46.574076856 +0100
@@ -25,6 +25,7 @@
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 ;;
@@ -208,6 +209,8 @@
sparc-*-*) gdb_target=sparc ;;
sparc64-*-*) gdb_target=sparc64 ;;
+spu*-*-*) gdb_target=spu ;;
+
xstormy16-*-*) gdb_target=xstormy16 ;;
v850*-*-elf) gdb_target=v850 ;;
diff -urN gdb-orig/gdb/Makefile.in gdb-head/gdb/Makefile.in
--- gdb-orig/gdb/Makefile.in 2006-11-10 02:28:35.000000000 +0100
+++ gdb-head/gdb/Makefile.in 2006-11-21 15:14:44.877068040 +0100
@@ -799,6 +799,7 @@
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
@@ -1496,6 +1497,7 @@
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 \
@@ -2718,6 +2720,15 @@
$(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 -urN gdb-orig/gdb/spu-linux-nat.c gdb-head/gdb/spu-linux-nat.c
--- gdb-orig/gdb/spu-linux-nat.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-linux-nat.c 2006-11-21 15:14:44.000000000 +0100
@@ -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 -urN gdb-orig/gdb/spu-tdep.c gdb-head/gdb/spu-tdep.c
--- gdb-orig/gdb/spu-tdep.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.c 2006-11-21 15:14:44.933059528 +0100
@@ -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, ®))
+ {
+ 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 -urN gdb-orig/gdb/spu-tdep.h gdb-head/gdb/spu-tdep.h
--- gdb-orig/gdb/spu-tdep.h 1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/spu-tdep.h 2006-11-21 15:14:44.000000000 +0100
@@ -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
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-21 20:22 ` Ulrich Weigand
@ 2006-11-21 20:40 ` Daniel Jacobowitz
2006-11-21 21:32 ` Mark Kettenis
1 sibling, 0 replies; 20+ messages in thread
From: Daniel Jacobowitz @ 2006-11-21 20:40 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Mark Kettenis, gdb-patches
On Tue, Nov 21, 2006 at 09:22:15PM +0100, Ulrich Weigand wrote:
> Suggestions? The cleanest way I could think of would be to actually
> extend the to_attach, to_detach, to_create_inferior, and to_mourn_inferior
> callbacks with an explict struct target_ops * parameter.
Yes, that's the right way to do it.
> However, that change would have extensive effects throughout GDB; many
> of the changes are mechanical, but some appear non-trivial, like the
> way the various thread stratum targets call back to the "beneath" target
> using saved function pointers ...
Yes, that's the reason it hasn't been done yet :-) When you have the
real target_ops, you can find the target beneath it to invoke fairly
easily (there are examples of this in e.g. to_xfer_partial). Then some
of the ugly saved function pointers can be removed. As for the ones
which replace a member of a target_ops vector with another and then
save the old one, I'm not sure what to pass to those.
(I've not looked at your actual patch from this message.)
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-21 20:22 ` Ulrich Weigand
2006-11-21 20:40 ` Daniel Jacobowitz
@ 2006-11-21 21:32 ` Mark Kettenis
2006-11-22 14:13 ` Ulrich Weigand
1 sibling, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2006-11-21 21:32 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Daniel Jacobowitz, gdb-patches
> I've now also tried out yet another alternative approach, which avoids
> the ptrace_ops_hack problem by supporting only the spu target again.
>
> It still cleanly separates out target vs. host files. However, it does
> require a change to configure.ac to allow configure.host to specify that
> a configuration should be treated as "native" even though host != target.
>
> This patch does work as well as the original one. Would this (or
> something along those lines) also be acceptable?
This looks fine to me. Thanks!
Mark
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-21 21:32 ` Mark Kettenis
@ 2006-11-22 14:13 ` Ulrich Weigand
2006-11-22 18:43 ` Daniel Jacobowitz
0 siblings, 1 reply; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-22 14:13 UTC (permalink / raw)
To: Mark Kettenis; +Cc: Daniel Jacobowitz, gdb-patches
Mark Kettenis wrote:
> > I've now also tried out yet another alternative approach, which avoids
> > the ptrace_ops_hack problem by supporting only the spu target again.
> >
> > It still cleanly separates out target vs. host files. However, it does
> > require a change to configure.ac to allow configure.host to specify that
> > a configuration should be treated as "native" even though host != target.
> >
> > This patch does work as well as the original one. Would this (or
> > something along those lines) also be acceptable?
>
> This looks fine to me. Thanks!
I've committed the patch now. Many thanks for reviewing!
Should I add the port to MAINTAINERS?
Bye,
Ulrich
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-22 14:13 ` Ulrich Weigand
@ 2006-11-22 18:43 ` Daniel Jacobowitz
2006-11-22 19:46 ` Ulrich Weigand
0 siblings, 1 reply; 20+ messages in thread
From: Daniel Jacobowitz @ 2006-11-22 18:43 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: Mark Kettenis, gdb-patches
On Wed, Nov 22, 2006 at 03:12:46PM +0100, Ulrich Weigand wrote:
> Should I add the port to MAINTAINERS?
Yes, please do. You're the obvious maintainer for it. If you haven't
added anything to NEWS yet, please do that also - this is an exciting
contribution!
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFA][3/5] New port: Cell BE SPU (the port itself)
2006-11-22 18:43 ` Daniel Jacobowitz
@ 2006-11-22 19:46 ` Ulrich Weigand
0 siblings, 0 replies; 20+ messages in thread
From: Ulrich Weigand @ 2006-11-22 19:46 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Mark Kettenis, gdb-patches
Daniel Jacobowitz wrote:
> Yes, please do. You're the obvious maintainer for it. If you haven't
> added anything to NEWS yet, please do that also - this is an exciting
> contribution!
Thanks! Here's what I've committed:
ChangeLog:
* NEWS: New port to Cell Broadband Engine SPU.
* MAINTAINERS: Add myself as spu maintainer.
Index: gdb/MAINTAINERS
===================================================================
RCS file: /cvs/src/src/gdb/MAINTAINERS,v
retrieving revision 1.351
diff -u -p -r1.351 MAINTAINERS
--- gdb/MAINTAINERS 16 Nov 2006 19:45:45 -0000 1.351
+++ gdb/MAINTAINERS 22 Nov 2006 19:39:33 -0000
@@ -303,6 +303,9 @@ the native maintainer when resolving ABI
sparc --target=sparc-elf ,-Werror
+ spu --target=spu-elf ,-Werror
+ Ulrich Weigand uweigand@de.ibm.com
+
v850 --target=v850-elf ,-Werror
vax --target=vax-netbsd ,-Werror
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.201
diff -u -p -r1.201 NEWS
--- gdb/NEWS 20 Nov 2006 00:16:05 -0000 1.201
+++ gdb/NEWS 22 Nov 2006 19:39:33 -0000
@@ -3,6 +3,10 @@
*** Changes since GDB 6.6
+* New targets
+
+Cell Broadband Engine SPU spu-elf
+
* The GDB remote stub, gdbserver, now has lower overhead for high
frequency signals (e.g. SIGALRM) via the QPassSignals packet.
Bye,
Ulrich
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2006-11-22 19:46 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-11-11 18:39 [RFA][3/5] New port: Cell BE SPU (the port itself) Ulrich Weigand
2006-11-11 21:19 ` Mark Kettenis
2006-11-12 15:38 ` Ulrich Weigand
2006-11-12 21:42 ` Mark Kettenis
2006-11-12 22:12 ` Daniel Jacobowitz
2006-11-13 12:27 ` Ulrich Weigand
2006-11-13 12:43 ` Mark Kettenis
2006-11-13 13:48 ` Daniel Jacobowitz
2006-11-13 19:50 ` Jim Blandy
2006-11-18 0:10 ` Ulrich Weigand
2006-11-18 6:03 ` Daniel Jacobowitz
2006-11-18 11:10 ` Ulrich Weigand
2006-11-18 16:41 ` Daniel Jacobowitz
2006-11-18 17:35 ` Mark Kettenis
2006-11-21 20:22 ` Ulrich Weigand
2006-11-21 20:40 ` Daniel Jacobowitz
2006-11-21 21:32 ` Mark Kettenis
2006-11-22 14:13 ` Ulrich Weigand
2006-11-22 18:43 ` Daniel Jacobowitz
2006-11-22 19:46 ` Ulrich Weigand
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox