From: nobody <> Date: Wed, 22 Nov 2006 17:34:16 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create branch 'gdb_6_6-branch'. X-Git-Tag: gdb_6_6-2006-12-18-release~42 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d06ed9bffb4ca22aa91be1b48375d5631aff793f;p=thirdparty%2Fbinutils-gdb.git This commit was manufactured by cvs2svn to create branch 'gdb_6_6-branch'. Cherrypick from master 2006-11-22 17:34:15 UTC Ulrich Weigand 'ChangeLog:': gdb/config/powerpc/spu-linux.mh gdb/config/spu/spu.mt gdb/gdbserver/spu-low.c gdb/regformats/reg-spu.dat gdb/spu-linux-nat.c gdb/spu-tdep.c gdb/spu-tdep.h gdb/testsuite/gdb.asm/spu.inc --- diff --git a/gdb/config/powerpc/spu-linux.mh b/gdb/config/powerpc/spu-linux.mh new file mode 100644 index 00000000000..068d294df13 --- /dev/null +++ b/gdb/config/powerpc/spu-linux.mh @@ -0,0 +1,7 @@ +# Target: Cell BE (PowerPC64 + SPU) + +# This implements a 'pseudo-native' GDB running on the +# PPU side of the Cell BE and debugging the SPU side. + +NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o + diff --git a/gdb/config/spu/spu.mt b/gdb/config/spu/spu.mt new file mode 100644 index 00000000000..2ea6fdc12c7 --- /dev/null +++ b/gdb/config/spu/spu.mt @@ -0,0 +1,2 @@ +# Target: Cell BE SPU +TDEPFILES= spu-tdep.o diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c new file mode 100644 index 00000000000..024a0525ec6 --- /dev/null +++ b/gdb/gdbserver/spu-low.c @@ -0,0 +1,585 @@ +/* Low level interface to SPUs, for the remote server for GDB. + Copyright (C) 2006 Free Software Foundation, Inc. + + Contributed by Ulrich Weigand . + + 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 "server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +#define PTRACE_TYPE_RET long +#define PTRACE_TYPE_ARG3 long + +/* Number of registers. */ +#define SPU_NUM_REGS 130 +#define SPU_NUM_CORE_REGS 128 + +/* Special registers. */ +#define SPU_ID_REGNUM 128 +#define SPU_PC_REGNUM 129 + +/* PPU side system calls. */ +#define INSTR_SC 0x44000002 +#define NR_spu_run 0x0116 + +/* Get current thread ID (Linux task ID). */ +#define current_tid ((struct inferior_list_entry *)current_inferior)->id + +/* These are used in remote-utils.c. */ +int using_threads = 0; +int debug_threads = 0; + + +/* Fetch PPU register REGNO. */ +static CORE_ADDR +fetch_ppc_register (int regno) +{ + PTRACE_TYPE_RET res; + + int tid = current_tid; + +#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 = current_tid; + + 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 = current_tid; + + 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 (*(unsigned int *)buf != 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 int +spu_proc_xfer_spu (const char *annex, unsigned char *readbuf, + const unsigned char *writebuf, + CORE_ADDR offset, int len) +{ + char buf[128]; + int fd = 0; + int ret = -1; + + if (!annex) + return 0; + + sprintf (buf, "/proc/%ld/fd/%s", current_tid, 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; +} + + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. */ +static int +spu_create_inferior (char *program, char **allargs) +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + ptrace (PTRACE_TRACEME, 0, 0, 0); + + setpgid (0, 0); + + execv (program, allargs); + + fprintf (stderr, "Cannot exec %s: %s.\n", program, + strerror (errno)); + fflush (stderr); + _exit (0177); + } + + add_thread (pid, NULL, pid); + return pid; +} + +/* Attach to an inferior process. */ +int +spu_attach (unsigned long pid) +{ + if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) + { + fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + strerror (errno), errno); + fflush (stderr); + _exit (0177); + } + + add_thread (pid, NULL, pid); + return 0; +} + +/* Kill the inferior process. */ +static void +spu_kill (void) +{ + ptrace (PTRACE_KILL, current_tid, 0, 0); +} + +/* Detach from inferior process. */ +static void +spu_detach (void) +{ + ptrace (PTRACE_DETACH, current_tid, 0, 0); +} + +/* Return nonzero if the given thread is still alive. */ +static int +spu_thread_alive (unsigned long tid) +{ + return tid == current_tid; +} + +/* Resume process. */ +static void +spu_resume (struct thread_resume *resume_info) +{ + while (resume_info->thread != -1 + && resume_info->thread != current_tid) + resume_info++; + + block_async_io (); + enable_async_io (); + + if (resume_info->leave_stopped) + return; + + /* We don't support hardware single-stepping right now, assume + GDB knows to use software single-stepping. */ + if (resume_info->step) + fprintf (stderr, "Hardware single-step not supported.\n"); + + regcache_invalidate (); + + errno = 0; + ptrace (PTRACE_CONT, current_tid, 0, resume_info->sig); + if (errno) + perror_with_name ("ptrace"); +} + +/* Wait for process, returns status. */ +static unsigned char +spu_wait (char *status) +{ + int tid = current_tid; + int w; + int ret; + + enable_async_io (); + unblock_async_io (); + + while (1) + { + ret = waitpid (tid, &w, WNOHANG | __WALL | __WNOTHREAD); + + if (ret == -1) + { + if (errno != ECHILD) + perror_with_name ("waitpid"); + } + else if (ret > 0) + break; + + usleep (1000); + } + + /* On the first wait, continue running the inferior until we are + blocked inside an spu_run system call. */ + if (!server_waiting) + { + int fd; + CORE_ADDR addr; + + while (!parse_spufs_run (&fd, &addr)) + { + ptrace (PT_SYSCALL, tid, (PTRACE_TYPE_ARG3) 0, 0); + waitpid (tid, NULL, __WALL | __WNOTHREAD); + } + } + + disable_async_io (); + + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + clear_inferiors (); + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + *status = 'X'; + clear_inferiors (); + return ((unsigned char) WTERMSIG (w)); + } + + /* After attach, we may have received a SIGSTOP. Do not return this + as signal to GDB, or else it will try to continue with SIGSTOP ... */ + if (!server_waiting) + { + *status = 'T'; + return 0; + } + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Fetch inferior registers. */ +static void +spu_fetch_registers (int regno) +{ + int fd; + CORE_ADDR addr; + + /* ??? Some callers use 0 to mean all registers. */ + if (regno == 0) + regno = -1; + + /* 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) + supply_register (SPU_ID_REGNUM, (char *)&fd); + + /* The NPC register is found at ADDR. */ + if (regno == -1 || regno == SPU_PC_REGNUM) + { + char buf[4]; + if (fetch_ppc_memory (addr, buf, 4) == 0) + supply_register (SPU_PC_REGNUM, buf); + } + + /* The GPRs are found in the "regs" spufs file. */ + if (regno == -1 || (regno >= 0 && regno < SPU_NUM_CORE_REGS)) + { + unsigned char 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++) + supply_register (i, buf + i*16); + } +} + +/* Store inferior registers. */ +static void +spu_store_registers (int regno) +{ + int fd; + CORE_ADDR addr; + + /* ??? Some callers use 0 to mean all registers. */ + if (regno == 0) + regno = -1; + + /* 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]; + collect_register (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)) + { + unsigned char buf[16*SPU_NUM_CORE_REGS]; + char annex[32]; + int i; + + for (i = 0; i < SPU_NUM_CORE_REGS; i++) + collect_register (i, buf + i*16); + + sprintf (annex, "%d/regs", fd); + spu_proc_xfer_spu (annex, NULL, buf, 0, sizeof buf); + } +} + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ +static int +spu_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int fd, ret; + CORE_ADDR addr; + char 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 (annex, "%d/mem", fd); + ret = spu_proc_xfer_spu (annex, myaddr, NULL, memaddr, len); + return ret == len ? 0 : EIO; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ +static int +spu_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) +{ + int fd, ret; + CORE_ADDR addr; + char 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 (annex, "%d/mem", fd); + ret = spu_proc_xfer_spu (annex, NULL, myaddr, memaddr, len); + return ret == len ? 0 : EIO; +} + +/* Look up special symbols -- unneded here. */ +static void +spu_look_up_symbols (void) +{ +} + +/* Send signal to inferior. */ +static void +spu_send_signal (int signo) +{ + syscall (SYS_tkill, current_tid, signo); +} + + +static struct target_ops spu_target_ops = { + spu_create_inferior, + spu_attach, + spu_kill, + spu_detach, + spu_thread_alive, + spu_resume, + spu_wait, + spu_fetch_registers, + spu_store_registers, + spu_read_memory, + spu_write_memory, + spu_look_up_symbols, + spu_send_signal, + NULL, +}; + +void +initialize_low (void) +{ + static const unsigned char breakpoint[] = { 0x00, 0x00, 0x3f, 0xff }; + + set_target_ops (&spu_target_ops); + set_breakpoint_data (breakpoint, sizeof breakpoint); + init_registers (); +} diff --git a/gdb/regformats/reg-spu.dat b/gdb/regformats/reg-spu.dat new file mode 100644 index 00000000000..6ba92b6db99 --- /dev/null +++ b/gdb/regformats/reg-spu.dat @@ -0,0 +1,132 @@ +name:spu +expedite:r0,r1,npc +128:r0 +128:r1 +128:r2 +128:r3 +128:r4 +128:r5 +128:r6 +128:r7 +128:r8 +128:r9 +128:r10 +128:r11 +128:r12 +128:r13 +128:r14 +128:r15 +128:r16 +128:r17 +128:r18 +128:r19 +128:r20 +128:r21 +128:r22 +128:r23 +128:r24 +128:r25 +128:r26 +128:r27 +128:r28 +128:r29 +128:r30 +128:r31 +128:r32 +128:r33 +128:r34 +128:r35 +128:r36 +128:r37 +128:r38 +128:r39 +128:r40 +128:r41 +128:r42 +128:r43 +128:r44 +128:r45 +128:r46 +128:r47 +128:r48 +128:r49 +128:r50 +128:r51 +128:r52 +128:r53 +128:r54 +128:r55 +128:r56 +128:r57 +128:r58 +128:r59 +128:r60 +128:r61 +128:r62 +128:r63 +128:r64 +128:r65 +128:r66 +128:r67 +128:r68 +128:r69 +128:r70 +128:r71 +128:r72 +128:r73 +128:r74 +128:r75 +128:r76 +128:r77 +128:r78 +128:r79 +128:r80 +128:r81 +128:r82 +128:r83 +128:r84 +128:r85 +128:r86 +128:r87 +128:r88 +128:r89 +128:r90 +128:r91 +128:r92 +128:r93 +128:r94 +128:r95 +128:r96 +128:r97 +128:r98 +128:r99 +128:r100 +128:r101 +128:r102 +128:r103 +128:r104 +128:r105 +128:r106 +128:r107 +128:r108 +128:r109 +128:r110 +128:r111 +128:r112 +128:r113 +128:r114 +128:r115 +128:r116 +128:r117 +128:r118 +128:r119 +128:r120 +128:r121 +128:r122 +128:r123 +128:r124 +128:r125 +128:r126 +128:r127 +32:npc +32:id diff --git a/gdb/spu-linux-nat.c b/gdb/spu-linux-nat.c new file mode 100644 index 00000000000..fd3673c6a23 --- /dev/null +++ b/gdb/spu-linux-nat.c @@ -0,0 +1,557 @@ +/* SPU native-dependent code for GDB, the GNU debugger. + Copyright (C) 2006 Free Software Foundation, Inc. + + Contributed by Ulrich Weigand . + + 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 +#include +#include +#include + +#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 (""), "elf32-spu", + spu_bfd_iovec_open, open_closure, + spu_bfd_iovec_pread, spu_bfd_iovec_close); + if (!nbfd) + return NULL; + + if (!bfd_check_format (nbfd, bfd_object)) + { + bfd_close (nbfd); + return NULL; + } + + return nbfd; +} + +/* INFERIOR_FD is a file handle passed by the inferior to the + spu_run system call. Assuming the SPE context was allocated + by the libspe library, try to retrieve the main SPE executable + file from its copy within the target process. */ +static void +spu_symbol_file_add_from_memory (int inferior_fd) +{ + CORE_ADDR addr; + struct bfd *nbfd; + + char id[128]; + char annex[32]; + int len; + + /* Read object ID. */ + xsnprintf (annex, sizeof annex, "%d/object-id", inferior_fd); + len = spu_proc_xfer_spu (annex, id, NULL, 0, sizeof id); + if (len <= 0 || len >= sizeof id) + return; + id[len] = 0; + if (sscanf (id, "0x%llx", &addr) != 1) + return; + + /* Open BFD representing SPE executable and read its symbols. */ + nbfd = spu_bfd_open (addr); + if (nbfd) + symbol_file_add_from_bfd (nbfd, 0, NULL, 1, 0); +} + + +/* Override the post_startup_inferior routine to continue running + the inferior until the first spu_run system call. */ +static void +spu_child_post_startup_inferior (ptid_t ptid) +{ + int fd; + CORE_ADDR addr; + + int tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + while (!parse_spufs_run (&fd, &addr)) + { + ptrace (PT_SYSCALL, tid, (PTRACE_TYPE_ARG3) 0, 0); + waitpid (tid, NULL, __WALL | __WNOTHREAD); + } +} + +/* Override the post_attach routine to try load the SPE executable + file image from its copy inside the target process. */ +static void +spu_child_post_attach (int pid) +{ + int fd; + CORE_ADDR addr; + + /* Like child_post_startup_inferior, if we happened to attach to + the inferior while it wasn't currently in spu_run, continue + running it until we get back there. */ + while (!parse_spufs_run (&fd, &addr)) + { + ptrace (PT_SYSCALL, pid, (PTRACE_TYPE_ARG3) 0, 0); + waitpid (pid, NULL, __WALL | __WNOTHREAD); + } + + /* If the user has not provided an executable file, try to extract + the image from inside the target process. */ + if (!get_exec_file (0)) + spu_symbol_file_add_from_memory (fd); +} + +/* Wait for child PTID to do something. Return id of the child, + minus_one_ptid in case of error; store status into *OURSTATUS. */ +static ptid_t +spu_child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) +{ + int save_errno; + int status; + pid_t pid; + + do + { + set_sigint_trap (); /* Causes SIGINT to be passed on to the + attached process. */ + set_sigio_trap (); + + pid = waitpid (PIDGET (ptid), &status, 0); + if (pid == -1 && errno == ECHILD) + /* Try again with __WCLONE to check cloned processes. */ + pid = waitpid (PIDGET (ptid), &status, __WCLONE); + + save_errno = errno; + + /* Make sure we don't report an event for the exit of the + original program, if we've detached from it. */ + if (pid != -1 && !WIFSTOPPED (status) && pid != PIDGET (inferior_ptid)) + { + pid = -1; + save_errno = EINTR; + } + + clear_sigio_trap (); + clear_sigint_trap (); + } + while (pid == -1 && save_errno == EINTR); + + if (pid == -1) + { + warning ("Child process unexpectedly missing: %s", + safe_strerror (save_errno)); + + /* Claim it exited with unknown signal. */ + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + return minus_one_ptid; + } + + store_waitstatus (ourstatus, status); + return pid_to_ptid (pid); +} + +/* Override the fetch_inferior_register routine. */ +static void +spu_fetch_inferior_registers (int regno) +{ + int fd; + CORE_ADDR addr; + + /* We must be stopped on a spu_run system call. */ + if (!parse_spufs_run (&fd, &addr)) + return; + + /* The ID register holds the spufs file handle. */ + if (regno == -1 || regno == SPU_ID_REGNUM) + { + char buf[4]; + store_unsigned_integer (buf, 4, fd); + regcache_raw_supply (current_regcache, SPU_ID_REGNUM, buf); + } + + /* The NPC register is found at ADDR. */ + if (regno == -1 || regno == SPU_PC_REGNUM) + { + gdb_byte buf[4]; + if (fetch_ppc_memory (addr, buf, 4) == 0) + regcache_raw_supply (current_regcache, SPU_PC_REGNUM, buf); + } + + /* The GPRs are found in the "regs" spufs file. */ + if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS)) + { + gdb_byte buf[16 * SPU_NUM_GPRS]; + char annex[32]; + int i; + + xsnprintf (annex, sizeof annex, "%d/regs", fd); + if (spu_proc_xfer_spu (annex, buf, NULL, 0, sizeof buf) == sizeof buf) + for (i = 0; i < SPU_NUM_GPRS; i++) + regcache_raw_supply (current_regcache, i, buf + i*16); + } +} + +/* Override the store_inferior_register routine. */ +static void +spu_store_inferior_registers (int regno) +{ + int fd; + CORE_ADDR addr; + + /* We must be stopped on a spu_run system call. */ + if (!parse_spufs_run (&fd, &addr)) + return; + + /* The NPC register is found at ADDR. */ + if (regno == -1 || regno == SPU_PC_REGNUM) + { + gdb_byte buf[4]; + regcache_raw_collect (current_regcache, SPU_PC_REGNUM, buf); + store_ppc_memory (addr, buf, 4); + } + + /* The GPRs are found in the "regs" spufs file. */ + if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS)) + { + gdb_byte buf[16 * SPU_NUM_GPRS]; + char annex[32]; + int i; + + for (i = 0; i < SPU_NUM_GPRS; i++) + regcache_raw_collect (current_regcache, i, buf + i*16); + + xsnprintf (annex, sizeof annex, "%d/regs", fd); + spu_proc_xfer_spu (annex, NULL, buf, 0, sizeof buf); + } +} + +/* Override the to_xfer_partial routine. */ +static LONGEST +spu_xfer_partial (struct target_ops *ops, + enum target_object object, const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (object == TARGET_OBJECT_MEMORY) + { + int fd; + CORE_ADDR addr; + char mem_annex[32]; + + /* We must be stopped on a spu_run system call. */ + if (!parse_spufs_run (&fd, &addr)) + return 0; + + /* Use the "mem" spufs file to access SPU local store. */ + xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd); + return spu_proc_xfer_spu (mem_annex, readbuf, writebuf, offset, len); + } + + return 0; +} + +/* Override the to_can_use_hw_breakpoint routine. */ +static int +spu_can_use_hw_breakpoint (int type, int cnt, int othertype) +{ + return 0; +} + + +/* Initialize SPU native target. */ +void +_initialize_spu_nat (void) +{ + /* Generic ptrace methods. */ + struct target_ops *t; + t = inf_ptrace_target (); + + /* Add SPU methods. */ + t->to_post_attach = spu_child_post_attach; + t->to_post_startup_inferior = spu_child_post_startup_inferior; + t->to_wait = spu_child_wait; + t->to_fetch_registers = spu_fetch_inferior_registers; + t->to_store_registers = spu_store_inferior_registers; + t->to_xfer_partial = spu_xfer_partial; + t->to_can_use_hw_breakpoint = spu_can_use_hw_breakpoint; + + /* Register SPU target. */ + add_target (t); +} + diff --git a/gdb/spu-tdep.c b/gdb/spu-tdep.c new file mode 100644 index 00000000000..ed88dd4fca0 --- /dev/null +++ b/gdb/spu-tdep.c @@ -0,0 +1,1099 @@ +/* SPU target-dependent code for GDB, the GNU debugger. + Copyright (C) 2006 Free Software Foundation, Inc. + + Contributed by Ulrich Weigand . + Based on a port by Sid Manning . + + 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 --git a/gdb/spu-tdep.h b/gdb/spu-tdep.h new file mode 100644 index 00000000000..8bf83092ead --- /dev/null +++ b/gdb/spu-tdep.h @@ -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 diff --git a/gdb/testsuite/gdb.asm/spu.inc b/gdb/testsuite/gdb.asm/spu.inc new file mode 100644 index 00000000000..aabceb845d2 --- /dev/null +++ b/gdb/testsuite/gdb.asm/spu.inc @@ -0,0 +1,42 @@ + comment "subroutine prologue" + .macro gdbasm_enter + stqd $lr, 16($sp) + stqd $sp, -32($sp) + ai $sp, $sp, -32 + .endm + + comment "subroutine epilogue" + .macro gdbasm_leave + ai $sp, $sp, 32 + lqd $lr, 16($sp) + bi $lr + .endm + + .macro gdbasm_call subr + brsl $lr, \subr + .endm + + .macro gdbasm_several_nops + nop + nop + nop + nop + .endm + + comment "exit (0)" + .macro gdbasm_exit0 + stop 0x2000 + stop + stop + stop + .endm + + comment "startup" + .macro gdbasm_startup + il $0, 0 + ila $sp, 16368 + stqd $0, 0($sp) + stqd $sp, -32($sp) + ai $sp, $sp, -32 + stqd $0, 16($sp) + .endm