summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorUlf Carlsson <md1ulfc@mdstud.chalmers.se>2000-03-24 00:07:29 +0000
committerUlf Carlsson <md1ulfc@mdstud.chalmers.se>2000-03-24 00:07:29 +0000
commit043082606f3247dbccac190f296190e83995cce1 (patch)
tree066aea3d2e23bef689c13339786f96061c46a938 /arch
parent16b5d462f73eb29d1f67fa01cc1ea66afdc72569 (diff)
o strace support
Diffstat (limited to 'arch')
-rw-r--r--arch/mips64/kernel/ptrace.c531
-rw-r--r--arch/mips64/kernel/scall_o32.S6
2 files changed, 532 insertions, 5 deletions
diff --git a/arch/mips64/kernel/ptrace.c b/arch/mips64/kernel/ptrace.c
index fb687f496..0301a66a3 100644
--- a/arch/mips64/kernel/ptrace.c
+++ b/arch/mips64/kernel/ptrace.c
@@ -1,4 +1,4 @@
-/* $Id: ptrace.c,v 1.1 1999/10/11 14:19:58 ralf Exp $
+/* $Id: ptrace.c,v 1.1 1999/12/04 03:59:00 ralf Exp $
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -8,13 +8,542 @@
* Copyright (C) Linus Torvalds
* Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle
* Copyright (C) 1996 David S. Miller
+ * Copyright (C) 2000 Ulf Carlsson
*
* At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
* binaries.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/user.h>
+#include <asm/mipsregs.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* Tracing a 32-bit process with a 64-bit strace and vice verca will not
+ work. I don't know how to fix this. */
+
+asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
+{
+ struct task_struct *child;
+ unsigned long flags;
+ int ret;
+ extern void save_fp(void*);
+
+ lock_kernel();
+#if 0
+ printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
+ (int) request, (int) pid, (unsigned long) addr,
+ (unsigned long) data);
+#endif
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->flags & PF_PTRACED)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->flags |= PF_PTRACED;
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+ read_unlock(&tasklist_lock); /* FIXME!!! */
+ if (!child)
+ goto out;
+ ret = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out;
+ if (request == PTRACE_ATTACH) {
+ if (child == current)
+ goto out;
+ if ((!child->dumpable ||
+ (current->uid != child->euid) ||
+ (current->uid != child->suid) ||
+ (current->uid != child->uid) ||
+ (current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+ (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out;
+ /* the same process cannot be attached many times */
+ if (child->flags & PF_PTRACED)
+ goto out;
+ child->flags |= PF_PTRACED;
+
+ write_lock_irqsave(&tasklist_lock, flags);
+ if (child->p_pptr != current) {
+ REMOVE_LINKS(child);
+ child->p_pptr = current;
+ SET_LINKS(child);
+ }
+ write_unlock_irqrestore(&tasklist_lock, flags);
+
+ send_sig(SIGSTOP, child, 1);
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ if (!(child->flags & PF_PTRACED))
+ goto out;
+ if (child->state != TASK_STOPPED) {
+ if (request != PTRACE_KILL)
+ goto out;
+ }
+ if (child->p_pptr != current)
+ goto out;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned int tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned int *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ struct pt_regs *regs;
+ unsigned int tmp;
+
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+ ret = 0;
+
+ switch (addr) {
+ case 0 ... 31:
+ tmp = regs->regs[addr];
+ break;
+ case FPR_BASE ... FPR_BASE + 31:
+ if (child->used_math) {
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ }
+ tmp = child->thread.fpu.hard.fp_regs[addr - 32];
+ } else {
+ tmp = -EIO;
+ }
+ break;
+ case PC:
+ tmp = regs->cp0_epc;
+ break;
+ case CAUSE:
+ tmp = regs->cp0_cause;
+ break;
+ case BADVADDR:
+ tmp = regs->cp0_badvaddr;
+ break;
+ case MMHI:
+ tmp = regs->hi;
+ break;
+ case MMLO:
+ tmp = regs->lo;
+ break;
+ case FPC_CSR:
+ tmp = child->thread.fpu.hard.control;
+ break;
+ case FPC_EIR: { /* implementation / version register */
+ unsigned int flags;
+ __save_flags(flags);
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
+ __restore_flags(flags);
+ break;
+ }
+ default:
+ tmp = 0;
+ ret = -EIO;
+ goto out;
+ }
+ ret = put_user(tmp, (unsigned *) data);
+ goto out;
+ }
+ /* when I and D space are separate, this will have to be fixed. */
+ 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))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: {
+ struct pt_regs *regs;
+ ret = 0;
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+
+ switch (addr) {
+ case 0 ... 31:
+ regs->regs[addr] = data;
+ break;
+ case FPR_BASE ... FPR_BASE + 31: {
+ unsigned long *fregs;
+ if (child->used_math) {
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ regs->cp0_status &= ~ST0_CU1;
+ }
+ } else {
+ /* FP not yet used */
+ memset(&child->thread.fpu.hard, ~0,
+ sizeof(child->thread.fpu.hard));
+ child->thread.fpu.hard.control = 0;
+ }
+ fregs = child->thread.fpu.hard.fp_regs;
+ fregs[addr - FPR_BASE] = data;
+ break;
+ }
+ case PC:
+ regs->cp0_epc = data;
+ break;
+ case MMHI:
+ regs->hi = data;
+ break;
+ case MMLO:
+ regs->lo = data;
+ break;
+ case FPC_CSR:
+ child->thread.fpu.hard.control = data;
+ break;
+ default:
+ /* The rest are not allowed. */
+ ret = -EIO;
+ break;
+ }
+ goto out;
+ }
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ ret = -EIO;
+ if ((unsigned int) data > _NSIG)
+ break;
+ if (request == PTRACE_SYSCALL)
+ child->flags |= PF_TRACESYS;
+ else
+ child->flags &= ~PF_TRACESYS;
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+/*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL: {
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_DETACH: { /* detach a process that was attached. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+ child->exit_code = data;
+ write_lock_irqsave(&tasklist_lock, flags);
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ write_unlock_irqrestore(&tasklist_lock, flags);
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EIO;
+ break;
+ }
+out:
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ unsigned long flags;
+ int ret;
+ extern void save_fp(void*);
+
+ lock_kernel();
+#if 0
+ printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
+ (int) request, (int) pid, (unsigned long) addr,
+ (unsigned long) data);
+#endif
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->flags & PF_PTRACED)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->flags |= PF_PTRACED;
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+ read_unlock(&tasklist_lock); /* FIXME!!! */
+ if (!child)
+ goto out;
+ ret = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out;
+ if (request == PTRACE_ATTACH) {
+ if (child == current)
+ goto out;
+ if ((!child->dumpable ||
+ (current->uid != child->euid) ||
+ (current->uid != child->suid) ||
+ (current->uid != child->uid) ||
+ (current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+ (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out;
+ /* the same process cannot be attached many times */
+ if (child->flags & PF_PTRACED)
+ goto out;
+ child->flags |= PF_PTRACED;
+
+ write_lock_irqsave(&tasklist_lock, flags);
+ if (child->p_pptr != current) {
+ REMOVE_LINKS(child);
+ child->p_pptr = current;
+ SET_LINKS(child);
+ }
+ write_unlock_irqrestore(&tasklist_lock, flags);
+
+ send_sig(SIGSTOP, child, 1);
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ if (!(child->flags & PF_PTRACED))
+ goto out;
+ if (child->state != TASK_STOPPED) {
+ if (request != PTRACE_KILL)
+ goto out;
+ }
+ if (child->p_pptr != current)
+ goto out;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ 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))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ struct pt_regs *regs;
+ unsigned long tmp;
+
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+ ret = 0;
+
+ switch (addr) {
+ case 0 ... 31:
+ tmp = regs->regs[addr];
+ break;
+ case FPR_BASE ... FPR_BASE + 31:
+ if (child->used_math) {
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ }
+ tmp = child->thread.fpu.hard.fp_regs[addr - 32];
+ } else {
+ tmp = -EIO;
+ }
+ break;
+ case PC:
+ tmp = regs->cp0_epc;
+ break;
+ case CAUSE:
+ tmp = regs->cp0_cause;
+ break;
+ case BADVADDR:
+ tmp = regs->cp0_badvaddr;
+ break;
+ case MMHI:
+ tmp = regs->hi;
+ break;
+ case MMLO:
+ tmp = regs->lo;
+ break;
+ case FPC_CSR:
+ tmp = child->thread.fpu.hard.control;
+ break;
+ case FPC_EIR: { /* implementation / version register */
+ unsigned int flags;
+ __save_flags(flags);
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
+ __restore_flags(flags);
+ break;
+ }
+ default:
+ tmp = 0;
+ ret = -EIO;
+ goto out;
+ }
+ ret = put_user(tmp, (unsigned long *) data);
+ goto out;
+ }
+ /* when I and D space are separate, this will have to be fixed. */
+ 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))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: {
+ struct pt_regs *regs;
+ ret = 0;
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+
+ switch (addr) {
+ case 0 ... 31:
+ regs->regs[addr] = data;
+ break;
+ case FPR_BASE ... FPR_BASE + 31: {
+ unsigned long *fregs;
+ if (child->used_math) {
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ regs->cp0_status &= ~ST0_CU1;
+ }
+ } else {
+ /* FP not yet used */
+ memset(&child->thread.fpu.hard, ~0,
+ sizeof(child->thread.fpu.hard));
+ child->thread.fpu.hard.control = 0;
+ }
+ fregs = child->thread.fpu.hard.fp_regs;
+ fregs[addr - FPR_BASE] = data;
+ break;
+ }
+ case PC:
+ regs->cp0_epc = data;
+ break;
+ case MMHI:
+ regs->hi = data;
+ break;
+ case MMLO:
+ regs->lo = data;
+ break;
+ case FPC_CSR:
+ child->thread.fpu.hard.control = data;
+ break;
+ default:
+ /* The rest are not allowed. */
+ ret = -EIO;
+ break;
+ }
+ goto out;
+ }
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ if (request == PTRACE_SYSCALL)
+ child->flags |= PF_TRACESYS;
+ else
+ child->flags &= ~PF_TRACESYS;
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+/*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL: {
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_DETACH: { /* detach a process that was attached. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+ child->exit_code = data;
+ write_lock_irqsave(&tasklist_lock, flags);
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ write_unlock_irqrestore(&tasklist_lock, flags);
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EIO;
+ break;
+ }
+out:
+ unlock_kernel();
+ return ret;
+}
asmlinkage void syscall_trace(void)
{
diff --git a/arch/mips64/kernel/scall_o32.S b/arch/mips64/kernel/scall_o32.S
index 4ffda9520..bae526c1f 100644
--- a/arch/mips64/kernel/scall_o32.S
+++ b/arch/mips64/kernel/scall_o32.S
@@ -1,4 +1,4 @@
-/* $Id: scall_o32.S,v 1.15 2000/03/20 22:56:30 kanoj Exp $
+/* $Id: scall_o32.S,v 1.16 2000/03/23 00:30:53 ulfc Exp $
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -20,8 +20,6 @@
#include <asm/stackframe.h>
#include <asm/unistd.h>
-#define DEBUG_MIPS64 1
-
/* This duplicates the definition from <linux/sched.h> */
#define PF_TRACESYS 0x00000020 /* tracing system calls */
@@ -220,7 +218,7 @@ illegal_syscall:
sys sys_setuid 1
sys sys_getuid 0
sys sys_stime 1 /* 4025 */
- sys sys_ni_syscall 0 /* ptrace */
+ sys sys32_ptrace 4
sys sys_alarm 1
sys sys_fstat 2
sys sys_ni_syscall 0