patch-2.4.20 linux-2.4.20/arch/parisc/kernel/ptrace.c
Next file: linux-2.4.20/arch/parisc/kernel/real1.c
Previous file: linux-2.4.20/arch/parisc/kernel/processor.c
Back to the patch index
Back to the overall index
- Lines: 315
- Date:
Thu Nov 28 15:53:10 2002
- Orig file:
linux-2.4.19/arch/parisc/kernel/ptrace.c
- Orig date:
Tue Sep 18 16:56:19 2001
diff -urN linux-2.4.19/arch/parisc/kernel/ptrace.c linux-2.4.20/arch/parisc/kernel/ptrace.c
@@ -14,6 +14,7 @@
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
+#include <linux/personality.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -31,6 +32,47 @@
#define PT_SINGLESTEP 0x10000
#define PT_BLOCKSTEP 0x20000
+/* PSW bits we allow the debugger to modify */
+#define USER_PSW_BITS (PSW_N | PSW_V | PSW_CB)
+
+#undef DEBUG_PTRACE
+
+#ifdef DEBUG_PTRACE
+#define DBG(x) printk x
+#else
+#define DBG(x)
+#endif
+
+#ifdef __LP64__
+
+#define CHILD_IS_32BIT (child->personality == PER_LINUX_32BIT)
+
+/* This function is needed to translate 32 bit pt_regs offsets in to
+ * 64 bit pt_regs offsets. For example, a 32 bit gdb under a 64 bit kernel
+ * will request offset 12 if it wants gr3, but the lower 32 bits of
+ * the 64 bit kernels view of gr3 will be at offset 28 (3*8 + 4).
+ * This code relies on a 32 bit pt_regs being comprised of 32 bit values
+ * except for the fp registers which (a) are 64 bits, and (b) follow
+ * the gr registers at the start of pt_regs. The 32 bit pt_regs should
+ * be half the size of the 64 bit pt_regs, plus 32*4 to allow for fr[]
+ * being 64 bit in both cases.
+ */
+
+static long translate_usr_offset(long offset)
+{
+ if (offset < 0)
+ return -1;
+ else if (offset <= 32*4) /* gr[0..31] */
+ return offset * 2 + 4;
+ else if (offset <= 32*4+32*8) /* gr[0..31] + fr[0..31] */
+ return offset + 32*4;
+ else if (offset < sizeof(struct pt_regs)/2 + 32*4)
+ return offset * 2 + 4 - 32*8;
+ else
+ return -1;
+}
+#endif
+
/*
* Called by kernel/ptrace.c when detaching..
*
@@ -49,6 +91,9 @@
{
struct task_struct *child;
long ret;
+#ifdef DEBUG_PTRACE
+ long oaddr=addr, odata=data;
+#endif
lock_kernel();
ret = -EPERM;
@@ -91,14 +136,33 @@
switch (request) {
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
- unsigned long tmp;
int copied;
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- goto out_tsk;
- ret = put_user(tmp,(unsigned long *) data);
+#ifdef __LP64__
+ if (CHILD_IS_32BIT) {
+ unsigned int tmp;
+
+ addr &= 0xffffffffL;
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ goto out_tsk;
+ ret = put_user(tmp,(unsigned int *) data);
+ DBG(("sys_ptrace(PEEK%s, %d, %lx, %lx) returning %ld, data %x\n",
+ request == PTRACE_PEEKTEXT ? "TEXT" : "DATA",
+ pid, oaddr, odata, ret, tmp));
+ }
+ else
+#endif
+ {
+ unsigned long tmp;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ goto out_tsk;
+ ret = put_user(tmp,(unsigned long *) data);
+ }
goto out_tsk;
}
@@ -106,22 +170,53 @@
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
- goto out_tsk;
+#ifdef __LP64__
+ if (CHILD_IS_32BIT) {
+ unsigned int tmp = (unsigned int)data;
+ DBG(("sys_ptrace(POKE%s, %d, %lx, %lx)\n",
+ request == PTRACE_POKETEXT ? "TEXT" : "DATA",
+ pid, oaddr, odata));
+ addr &= 0xffffffffL;
+ if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1) == sizeof(tmp))
+ goto out_tsk;
+ }
+ else
+#endif
+ {
+ if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+ goto out_tsk;
+ }
ret = -EIO;
goto out_tsk;
- /* Read the word at location addr in the USER area. This will need
- to change when the kernel no longer saves all regs on a syscall. */
+ /* Read the word at location addr in the USER area. For ptraced
+ processes, the kernel saves all regs on a syscall. */
case PTRACE_PEEKUSR: {
- unsigned long tmp;
-
ret = -EIO;
- if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs))
- goto out_tsk;
-
- tmp = *(unsigned long *) ((char *) task_regs(child) + addr);
- ret = put_user(tmp, (unsigned long *) data);
+#ifdef __LP64__
+ if (CHILD_IS_32BIT) {
+ unsigned int tmp;
+
+ if (addr & (sizeof(int)-1))
+ goto out_tsk;
+ if ((addr = translate_usr_offset(addr)) < 0)
+ goto out_tsk;
+
+ tmp = *(unsigned int *) ((char *) task_regs(child) + addr);
+ ret = put_user(tmp, (unsigned int *) data);
+ DBG(("sys_ptrace(PEEKUSR, %d, %lx, %lx) returning %ld, addr %lx, data %x\n",
+ pid, oaddr, odata, ret, addr, tmp));
+ }
+ else
+#endif
+ {
+ unsigned long tmp;
+
+ if ((addr & (sizeof(long)-1)) || (unsigned long) addr >= sizeof(struct pt_regs))
+ goto out_tsk;
+ tmp = *(unsigned long *) ((char *) task_regs(child) + addr);
+ ret = put_user(tmp, (unsigned long *) data);
+ }
goto out_tsk;
}
@@ -133,25 +228,73 @@
exit. */
case PTRACE_POKEUSR:
ret = -EIO;
- if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs))
- goto out_tsk;
- /* XXX This test probably needs adjusting. We probably want to
- * allow writes to some bits of PSW, and may want to block writes
- * to (some) space registers. Some register values written here
- * may be ignored in entry.S:syscall_restore_rfi; e.g. iaoq is
- * written with r31/r31+4, and not with the values in pt_regs.
+ /* Some register values written here may be ignored in
+ * entry.S:syscall_restore_rfi; e.g. iaoq is written with
+ * r31/r31+4, and not with the values in pt_regs.
+ */
+ /* PT_PSW=0, so this is valid for 32 bit processes under 64
+ * bit kernels.
*/
- /* Allow writing of gr1-gr31, fr*, sr*, iasq*, iaoq*, sar */
- if (addr == PT_PSW || (addr > PT_IAOQ1 && addr != PT_SAR))
+ if (addr == PT_PSW) {
+ /* PT_PSW=0, so this is valid for 32 bit processes
+ * under 64 bit kernels.
+ *
+ * Allow writing to Nullify, Divide-step-correction,
+ * and carry/borrow bits.
+ * BEWARE, if you set N, and then single step, it wont
+ * stop on the nullified instruction.
+ */
+ DBG(("sys_ptrace(POKEUSR, %d, %lx, %lx)\n",
+ pid, oaddr, odata));
+ data &= USER_PSW_BITS;
+ task_regs(child)->gr[0] &= ~USER_PSW_BITS;
+ task_regs(child)->gr[0] |= data;
+ ret = 0;
goto out_tsk;
-
- *(unsigned long *) ((char *) task_regs(child) + addr) = data;
- ret = 0;
- goto out_tsk;
+ }
+#ifdef __LP64__
+ if (CHILD_IS_32BIT) {
+ if (addr & (sizeof(int)-1))
+ goto out_tsk;
+ if ((addr = translate_usr_offset(addr)) < 0)
+ goto out_tsk;
+ DBG(("sys_ptrace(POKEUSR, %d, %lx, %lx) addr %lx\n",
+ pid, oaddr, odata, addr));
+ if (addr >= PT_FR0 && addr <= PT_FR31) {
+ /* Special case, fp regs are 64 bits anyway */
+ *(unsigned int *) ((char *) task_regs(child) + addr) = data;
+ ret = 0;
+ }
+ else if ((addr >= PT_GR1+4 && addr <= PT_GR31+4) ||
+ addr == PT_IAOQ0+4 || addr == PT_IAOQ1+4 ||
+ addr == PT_SAR+4) {
+ /* Zero the top 32 bits */
+ *(unsigned int *) ((char *) task_regs(child) + addr - 4) = 0;
+ *(unsigned int *) ((char *) task_regs(child) + addr) = data;
+ ret = 0;
+ }
+ goto out_tsk;
+ }
+ else
+#endif
+ {
+ if ((addr & (sizeof(long)-1)) || (unsigned long) addr >= sizeof(struct pt_regs))
+ goto out_tsk;
+ if ((addr >= PT_GR1 && addr <= PT_GR31) ||
+ addr == PT_IAOQ0 || addr == PT_IAOQ1 ||
+ (addr >= PT_FR0 && addr <= PT_FR31) ||
+ addr == PT_SAR) {
+ *(unsigned long *) ((char *) task_regs(child) + addr) = data;
+ ret = 0;
+ }
+ goto out_tsk;
+ }
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT:
ret = -EIO;
+ DBG(("sys_ptrace(%s)\n",
+ request == PTRACE_SYSCALL ? "SYSCALL" : "CONT"));
if ((unsigned long) data > _NSIG)
goto out_tsk;
child->ptrace &= ~(PT_SINGLESTEP|PT_BLOCKSTEP);
@@ -168,12 +311,14 @@
* sigkill. perhaps it should be put in the status
* that it wants to exit.
*/
+ DBG(("sys_ptrace(KILL)\n"));
if (child->state == TASK_ZOMBIE) /* already dead */
goto out_tsk;
child->exit_code = SIGKILL;
goto out_wake_notrap;
case PTRACE_SINGLEBLOCK:
+ DBG(("sys_ptrace(SINGLEBLOCK)\n"));
ret = -EIO;
if ((unsigned long) data > _NSIG)
goto out_tsk;
@@ -189,6 +334,7 @@
goto out_wake;
case PTRACE_SINGLESTEP:
+ DBG(("sys_ptrace(SINGLESTEP)\n"));
ret = -EIO;
if ((unsigned long) data > _NSIG)
goto out_tsk;
@@ -208,10 +354,7 @@
pa_psw(child)->y = 0;
pa_psw(child)->z = 0;
pa_psw(child)->b = 0;
- pa_psw(child)->r = 0;
- pa_psw(child)->t = 0;
- pa_psw(child)->h = 0;
- pa_psw(child)->l = 0;
+ ptrace_disable(child);
/* Don't wake up the child, but let the
parent know something happened. */
si.si_code = TRAP_TRACE;
@@ -249,11 +392,7 @@
}
out_wake_notrap:
- /* make sure the trap bits are not set */
- pa_psw(child)->r = 0;
- pa_psw(child)->t = 0;
- pa_psw(child)->h = 0;
- pa_psw(child)->l = 0;
+ ptrace_disable(child);
out_wake:
wake_up_process(child);
ret = 0;
@@ -261,6 +400,8 @@
free_task_struct(child);
out:
unlock_kernel();
+ DBG(("sys_ptrace(%ld, %d, %lx, %lx) returning %ld\n",
+ request, pid, oaddr, odata, ret));
return ret;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)