Commit e27b7a73 authored by Hirokazu Takata's avatar Hirokazu Takata Committed by Linus Torvalds

[PATCH] m32r: support PTRACE_GETREGS and PTRACE_SETREGS

Add PTRACE_GETREGS and PTRACE_SETREGS functions for the native debugging
support of the GNU Project Debugger, GDB.
Signed-off-by: default avatarHirokazu Takata <takata@linux-m32r.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent df3eebb1
/* /*
* linux/arch/sh/kernel/ptrace.c * linux/arch/m32r/kernel/ptrace.c
* *
* Copyright (C) 2002 Hirokazu Takata, Takeo Takahashi * Copyright (C) 2002 Hirokazu Takata, Takeo Takahashi
* Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org>
* *
* Original x86 implementation: * Original x86 implementation:
* By Ross Biro 1/23/92 * By Ross Biro 1/23/92
...@@ -9,11 +10,10 @@ ...@@ -9,11 +10,10 @@
* *
* Some code taken from sh version: * Some code taken from sh version:
* Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
* * Some code taken from arm version:
* Copyright (C) 2000 Russell King
*/ */
/* $Id$ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -33,60 +33,213 @@ ...@@ -33,60 +33,213 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#define DEBUG_PTRACE 0
/* /*
* does not yet catch signals sent when the child dies. * Get the address of the live pt_regs for the specified task.
* in exit.c or in signal.c. * These are saved onto the top kernel stack when the process
* is not running.
*
* Note: if a user thread is execve'd from kernel space, the
* kernel stack will not be empty on entry to the kernel, so
* ptracing these tasks will fail.
*/ */
static inline struct pt_regs *
get_user_regs(struct task_struct *task)
{
return (struct pt_regs *)
((unsigned long)task->thread_info + THREAD_SIZE
- sizeof(struct pt_regs));
}
/* /*
* This routine will get a word off of the process kernel stack. * This routine will get a word off of the process kernel stack.
*/ */
static __inline__ unsigned long int get_stack_long(struct task_struct *task, static inline unsigned long int
int offset) get_stack_long(struct task_struct *task, int offset)
{ {
unsigned long *stack;
unsigned char *stack; stack = (unsigned long *)get_user_regs(task);
stack = (unsigned char *)(task->thread_info) + THREAD_SIZE
- sizeof(struct pt_regs);
stack += offset;
return *((unsigned long *)stack); return stack[offset];
} }
/* /*
* This routine will put a word on the process kernel stack. * This routine will put a word on the process kernel stack.
*/ */
static __inline__ int put_stack_long(struct task_struct *task, int offset, static inline int
unsigned long data) put_stack_long(struct task_struct *task, int offset, unsigned long data)
{ {
unsigned char *stack; unsigned long *stack;
stack = (unsigned char *)(task->thread_info) + THREAD_SIZE stack = (unsigned long *)get_user_regs(task);
- sizeof(struct pt_regs); stack[offset] = data;
stack += offset;
*((unsigned long *)stack) = data;
return 0; return 0;
} }
static int reg_offset[] = { static int reg_offset[] = {
(4 * PT_R0), (4 * PT_R1), (4 * PT_R2), (4 * PT_R3), PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
(4 * PT_R4), (4 * PT_R5), (4 * PT_R6), (4 * PT_R7), PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_FP, PT_LR, PT_SPU,
(4 * PT_R8), (4 * PT_R9), (4 * PT_R10), (4 * PT_R11),
(4 * PT_R12), (4 * PT_FP), (4 * PT_LR), (4 * PT_SPU),
}; };
static __inline__ int /*
* Read the word at offset "off" into the "struct user". We
* actually access the pt_regs stored on the kernel stack.
*/
static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
unsigned long __user *data)
{
unsigned long tmp;
#ifndef NO_FPU
struct user * dummy = NULL;
#endif
if ((off & 3) || (off < 0) || (off > sizeof(struct user) - 3))
return -EIO;
off >>= 2;
switch (off) {
case PT_EVB:
__asm__ __volatile__ (
"mvfc %0, cr5 \n\t"
: "=r" (tmp)
);
break;
case PT_CBR: {
unsigned long psw;
psw = get_stack_long(tsk, PT_PSW);
tmp = ((psw >> 8) & 1);
}
break;
case PT_PSW: {
unsigned long psw, bbpsw;
psw = get_stack_long(tsk, PT_PSW);
bbpsw = get_stack_long(tsk, PT_BBPSW);
tmp = ((psw >> 8) & 0xff) | ((bbpsw & 0xff) << 8);
}
break;
case PT_PC:
tmp = get_stack_long(tsk, PT_BPC);
break;
case PT_BPC:
off = PT_BBPC;
/* fall through */
default:
if (off < (sizeof(struct pt_regs) >> 2))
tmp = get_stack_long(tsk, off);
#ifndef NO_FPU
else if (off >= (long)(&dummy->fpu >> 2) &&
off < (long)(&dummy->u_fpvalid >> 2)) {
if (!tsk->used_math) {
if (off == (long)(&dummy->fpu.fpscr >> 2))
tmp = FPSCR_INIT;
else
tmp = 0;
} else
tmp = ((long *)(&tsk->thread.fpu >> 2))
[off - (long)&dummy->fpu];
} else if (off == (long)(&dummy->u_fpvalid >> 2))
tmp = tsk->used_math;
#endif /* not NO_FPU */
else
tmp = 0;
}
return put_user(tmp, data);
}
static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
unsigned long data)
{
int ret = -EIO;
#ifndef NO_FPU
struct user * dummy = NULL;
#endif
if ((off & 3) || off < 0 ||
off > sizeof(struct user) - 3)
return -EIO;
off >>= 2;
switch (off) {
case PT_EVB:
case PT_BPC:
case PT_SPI:
/* We don't allow to modify evb. */
ret = 0;
break;
case PT_PSW:
case PT_CBR: {
/* We allow to modify only cbr in psw */
unsigned long psw;
psw = get_stack_long(tsk, PT_PSW);
psw = (psw & ~0x100) | ((data & 1) << 8);
ret = put_stack_long(tsk, PT_PSW, psw);
}
break;
case PT_PC:
off = PT_BPC;
data &= ~1;
/* fall through */
default:
if (off < (sizeof(struct pt_regs) >> 2))
ret = put_stack_long(tsk, off, data);
#ifndef NO_FPU
else if (off >= (long)(&dummy->fpu >> 2) &&
off < (long)(&dummy->u_fpvalid >> 2)) {
tsk->used_math = 1;
((long *)&tsk->thread.fpu)
[off - (long)&dummy->fpu] = data;
ret = 0;
} else if (off == (long)(&dummy->u_fpvalid >> 2)) {
tsk->used_math = data ? 1 : 0;
ret = 0;
}
#endif /* not NO_FPU */
break;
}
return ret;
}
/*
* Get all user integer registers.
*/
static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
{
struct pt_regs *regs = get_user_regs(tsk);
return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
}
/*
* Set all user integer registers.
*/
static int ptrace_setregs(struct task_struct *tsk, void __user *uregs)
{
struct pt_regs newregs;
int ret;
ret = -EFAULT;
if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) {
struct pt_regs *regs = get_user_regs(tsk);
*regs = newregs;
ret = 0;
}
return ret;
}
static inline int
check_condition_bit(struct task_struct *child) check_condition_bit(struct task_struct *child)
{ {
return (int)((get_stack_long(child, (4 * PT_PSW)) >> 8) & 1); return (int)((get_stack_long(child, PT_PSW) >> 8) & 1);
} }
static int static int
check_condition_src(unsigned long op, unsigned long regno1, unsigned long regno2, struct task_struct *child) check_condition_src(unsigned long op, unsigned long regno1,
unsigned long regno2, struct task_struct *child)
{ {
unsigned long reg1, reg2; unsigned long reg1, reg2;
...@@ -119,7 +272,8 @@ check_condition_src(unsigned long op, unsigned long regno1, unsigned long regno2 ...@@ -119,7 +272,8 @@ check_condition_src(unsigned long op, unsigned long regno1, unsigned long regno2
static void static void
compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc,
unsigned long *next_pc, struct task_struct *child) unsigned long *next_pc,
struct task_struct *child)
{ {
unsigned long op, op2, op3; unsigned long op, op2, op3;
unsigned long disp; unsigned long disp;
...@@ -182,21 +336,23 @@ compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, ...@@ -182,21 +336,23 @@ compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc,
return; return;
#endif #endif
} else if (op3 == 0xd) { /* RTE */ } else if (op3 == 0xd) { /* RTE */
*next_pc = get_stack_long(child, (4 * PT_BPC)); *next_pc = get_stack_long(child, PT_BPC);
return; return;
} }
break; break;
case 0xc: /* JC */ case 0xc: /* JC */
if (op3 == 0xc && check_condition_bit(child)) { if (op3 == 0xc && check_condition_bit(child)) {
regno = insn & 0xf; regno = insn & 0xf;
*next_pc = get_stack_long(child, reg_offset[regno]); *next_pc = get_stack_long(child,
reg_offset[regno]);
return; return;
} }
break; break;
case 0xd: /* JNC */ case 0xd: /* JNC */
if (op3 == 0xc && !check_condition_bit(child)) { if (op3 == 0xc && !check_condition_bit(child)) {
regno = insn & 0xf; regno = insn & 0xf;
*next_pc = get_stack_long(child, reg_offset[regno]); *next_pc = get_stack_long(child,
reg_offset[regno]);
return; return;
} }
break; break;
...@@ -204,7 +360,8 @@ compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, ...@@ -204,7 +360,8 @@ compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc,
case 0xf: /* JMP */ case 0xf: /* JMP */
if (op3 == 0xc) { /* JMP */ if (op3 == 0xc) { /* JMP */
regno = insn & 0xf; regno = insn & 0xf;
*next_pc = get_stack_long(child, reg_offset[regno]); *next_pc = get_stack_long(child,
reg_offset[regno]);
return; return;
} }
break; break;
...@@ -218,7 +375,8 @@ compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, ...@@ -218,7 +375,8 @@ compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc,
static void static void
compute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc, compute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc,
unsigned long *next_pc, struct task_struct *child) unsigned long *next_pc,
struct task_struct *child)
{ {
unsigned long op; unsigned long op;
unsigned long op2; unsigned long op2;
...@@ -275,7 +433,7 @@ compute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc, ...@@ -275,7 +433,7 @@ compute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc,
*next_pc = pc + 4; *next_pc = pc + 4;
} }
static __inline__ void static inline void
compute_next_pc(unsigned long insn, unsigned long pc, compute_next_pc(unsigned long insn, unsigned long pc,
unsigned long *next_pc, struct task_struct *child) unsigned long *next_pc, struct task_struct *child)
{ {
...@@ -330,7 +488,8 @@ int withdraw_debug_trap_for_signal(struct task_struct *child) ...@@ -330,7 +488,8 @@ int withdraw_debug_trap_for_signal(struct task_struct *child)
} }
static int static int
unregister_debug_trap(struct task_struct *child, unsigned long addr, unsigned long *code) unregister_debug_trap(struct task_struct *child, unsigned long addr,
unsigned long *code)
{ {
struct debug_trap *p = &child->thread.debug_trap; struct debug_trap *p = &child->thread.debug_trap;
...@@ -360,7 +519,7 @@ unregister_all_debug_traps(struct task_struct *child) ...@@ -360,7 +519,7 @@ unregister_all_debug_traps(struct task_struct *child)
} }
} }
static void static inline void
invalidate_cache(void) invalidate_cache(void)
{ {
#if defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_OPSP) #if defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_OPSP)
...@@ -423,7 +582,7 @@ embed_debug_trap_for_signal(struct task_struct *child) ...@@ -423,7 +582,7 @@ embed_debug_trap_for_signal(struct task_struct *child)
unsigned long pc, insn; unsigned long pc, insn;
int ret; int ret;
pc = get_stack_long(child, (4 * PT_BPC)); pc = get_stack_long(child, PT_BPC);
ret = access_process_vm(child, pc&~3, &insn, sizeof(insn), 0); ret = access_process_vm(child, pc&~3, &insn, sizeof(insn), 0);
if (ret != sizeof(insn)) { if (ret != sizeof(insn)) {
printk("kernel BUG at %s %d: access_process_vm returns %d\n", printk("kernel BUG at %s %d: access_process_vm returns %d\n",
...@@ -458,6 +617,15 @@ withdraw_debug_trap(struct pt_regs *regs) ...@@ -458,6 +617,15 @@ withdraw_debug_trap(struct pt_regs *regs)
} }
} }
static void
init_debug_traps(struct task_struct *child)
{
struct debug_trap *p = &child->thread.debug_trap;
p->nr_trap = 0;
p->addr = 0;
p->insn = 0;
}
/* /*
* Called by kernel/ptrace.c when detaching.. * Called by kernel/ptrace.c when detaching..
...@@ -469,228 +637,61 @@ void ptrace_disable(struct task_struct *child) ...@@ -469,228 +637,61 @@ void ptrace_disable(struct task_struct *child)
/* nothing to do.. */ /* nothing to do.. */
} }
static void static int
init_debug_traps(struct task_struct *child) do_ptrace(long request, struct task_struct *child, long addr, long data)
{
struct debug_trap *p = &child->thread.debug_trap;
p->nr_trap = 0;
p->addr = 0;
p->insn = 0;
}
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{ {
struct task_struct *child; unsigned long tmp;
int ret; int ret;
#ifndef NO_FPU
struct user * dummy = NULL;
#endif
lock_kernel();
ret = -EPERM;
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->ptrace & PT_PTRACED)
goto out;
/* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
ret = 0;
goto out;
}
ret = -ESRCH;
read_lock(&tasklist_lock);
child = find_task_by_pid(pid);
if (child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
goto out;
if (request == PTRACE_ATTACH) {
#if (DEBUG_PTRACE > 1)
printk("ptrace: PTRACE_ATTACH: child:%08lx\n", (unsigned long)child);
#endif
ret = ptrace_attach(child);
if (ret == 0)
init_debug_traps(child);
goto out_tsk;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL);
if (ret < 0)
goto out_tsk;
switch (request) { switch (request) {
/* when I and D space are separate, these will need to be fixed. */ /*
case PTRACE_PEEKTEXT: /* read word at location addr. */ * read word at location "addr" in the child process.
case PTRACE_PEEKDATA: { */
unsigned long tmp; case PTRACE_PEEKTEXT:
int copied; case PTRACE_PEEKDATA:
ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
#if (DEBUG_PTRACE > 1) if (ret == sizeof(tmp))
printk("ptrace: PTRACE_PEEKTEXT/PEEKDATA: child:%08lx, addr:%08lx\n",
(unsigned long)child, addr);
#endif
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
ret = -EIO;
if (copied != sizeof(tmp))
break;
ret = put_user(tmp,(unsigned long __user *) data); ret = put_user(tmp,(unsigned long __user *) data);
break; else
}
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
#if DEBUG_PTRACE
printk("ptrace: PTRACE_PEEKUSER: child:%08lx, addr:%08lx\n",
(unsigned long)child, addr);
#endif
ret = -EIO; ret = -EIO;
if ((addr & 3) || addr < 0 ||
addr > sizeof(struct user) - 3)
break;
switch (addr) {
case (4 * PT_BPC):
addr = (4 * PT_BBPC);
break;
case (4 * PT_EVB):
__asm__ __volatile__ (
"mvfc %0, cr5\n"
:"=r"(tmp)
:
);
ret = put_user(tmp, (unsigned long __user *)data);
goto out_tsk;
break;
case (4 * PT_CBR): {
unsigned long psw;
psw = get_stack_long(child, (4 * PT_PSW));
tmp = ((psw >> 8) & 1);
ret = put_user(tmp, (unsigned long __user *)data);
goto out_tsk;
}
break;
case (4 * PT_PSW): {
unsigned long psw, bbpsw;
psw = get_stack_long(child, (4 * PT_PSW));
bbpsw = get_stack_long(child, (4 * PT_BBPSW));
tmp = ((psw >> 8) & 0xff) | ((bbpsw & 0xff) << 8);
ret = put_user(tmp, (unsigned long __user *)data);
goto out_tsk;
}
break;
case (4 * PT_PC): {
unsigned long pc;
pc = get_stack_long(child, (4 * PT_BPC));
ret = put_user(pc, (unsigned long __user *)data);
goto out_tsk;
}
break; break;
}
if (addr < sizeof(struct pt_regs)) /*
tmp = get_stack_long(child, addr); * read the word at location addr in the USER area.
#ifndef NO_FPU */
else if (addr >= (long) &dummy->fpu && case PTRACE_PEEKUSR:
addr < (long) &dummy->u_fpvalid) { ret = ptrace_read_user(child, addr,
if (!child->used_math) { (unsigned long __user *)data);
if (addr == (long)&dummy->fpu.fpscr)
tmp = FPSCR_INIT;
else
tmp = 0;
} else
tmp = ((long *)&child->thread.fpu)
[(addr - (long)&dummy->fpu) >> 2];
} else if (addr == (long) &dummy->u_fpvalid)
tmp = child->used_math;
#endif /* not NO_FPU */
else
tmp = 0;
ret = put_user(tmp, (unsigned long __user *)data);
break; break;
}
/* when I and D space are separate, this will have to be fixed. */ /*
case PTRACE_POKETEXT: /* write the word at location addr. */ * write the word at location addr.
*/
case PTRACE_POKETEXT:
case PTRACE_POKEDATA: case PTRACE_POKEDATA:
#if DEBUG_PTRACE ret = access_process_vm(child, addr, &data, sizeof(data), 1);
printk("ptrace: PTRACE_POKETEXT/POKEDATA: child:%08lx, addr:%08lx, data:%08lx\n", if (ret == sizeof(data)) {
(unsigned long)child, addr, data);
#endif
ret = -EIO;
if (access_process_vm(child, addr, &data, sizeof(data), 1)
!= sizeof(data)) {
break;
}
ret = 0; ret = 0;
if (request == PTRACE_POKETEXT) { if (request == PTRACE_POKETEXT) {
invalidate_cache(); invalidate_cache();
} }
break; } else {
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
#if DEBUG_PTRACE
printk("ptrace: PTRACE_POKEUSR: child:%08lx, addr:%08lx, data:%08lx\n",
(unsigned long)child, addr, data);
#endif
ret = -EIO; ret = -EIO;
if ((addr & 3) || addr < 0 ||
addr > sizeof(struct user) - 3)
break;
switch (addr) {
case (4 * PT_EVB):
case (4 * PT_BPC):
case (4 * PT_SPI):
/* We don't allow to modify evb. */
ret = 0;
goto out_tsk;
break;
case (4 * PT_PSW):
case (4 * PT_CBR): {
/* We allow to modify only cbr in psw */
unsigned long psw;
psw = get_stack_long(child, (4 * PT_PSW));
psw = (psw & ~0x100) | ((data & 1) << 8);
ret = put_stack_long(child, (4 * PT_PSW), psw);
goto out_tsk;
} }
break; break;
case (4 * PT_PC):
addr = (4 * PT_BPC);
data &= ~1;
break;
}
if (addr < sizeof(struct pt_regs)) /*
ret = put_stack_long(child, addr, data); * write the word at location addr in the USER area.
#ifndef NO_FPU */
else if (addr >= (long) &dummy->fpu && case PTRACE_POKEUSR:
addr < (long) &dummy->u_fpvalid) { ret = ptrace_write_user(child, addr, data);
child->used_math = 1;
((long *)&child->thread.fpu)
[(addr - (long)&dummy->fpu) >> 2] = data;
ret = 0;
} else if (addr == (long) &dummy->u_fpvalid) {
child->used_math = data?1:0;
ret = 0;
}
#endif /* not NO_FPU */
break; break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ /*
case PTRACE_CONT: { /* restart after signal. */ * continue/restart and stop at next (return from) syscall
#if DEBUG_PTRACE */
printk("ptrace: PTRACE_SYSCALL/CONT: child:%08lx, data:%08lx\n", case PTRACE_SYSCALL:
(unsigned long)child, data); case PTRACE_CONT:
#endif
ret = -EIO; ret = -EIO;
if ((unsigned long) data > _NSIG) if ((unsigned long) data > _NSIG)
break; break;
...@@ -702,17 +703,13 @@ printk("ptrace: PTRACE_SYSCALL/CONT: child:%08lx, data:%08lx\n", ...@@ -702,17 +703,13 @@ printk("ptrace: PTRACE_SYSCALL/CONT: child:%08lx, data:%08lx\n",
wake_up_process(child); wake_up_process(child);
ret = 0; ret = 0;
break; break;
}
/* /*
* make the child exit. Best I can do is send it a sigkill. * 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 * perhaps it should be put in the status that it wants to
* exit. * exit.
*/ */
case PTRACE_KILL: { case PTRACE_KILL: {
#if DEBUG_PTRACE
printk("ptrace: PTRACE_KILL: child:%08lx\n", (unsigned long)child);
#endif
ret = 0; ret = 0;
unregister_all_debug_traps(child); unregister_all_debug_traps(child);
invalidate_cache(); invalidate_cache();
...@@ -723,7 +720,10 @@ printk("ptrace: PTRACE_KILL: child:%08lx\n", (unsigned long)child); ...@@ -723,7 +720,10 @@ printk("ptrace: PTRACE_KILL: child:%08lx\n", (unsigned long)child);
break; break;
} }
case PTRACE_SINGLESTEP: { /* set the trap flag. */ /*
* execute single instruction.
*/
case PTRACE_SINGLESTEP: {
unsigned long next_pc; unsigned long next_pc;
unsigned long pc, insn; unsigned long pc, insn;
...@@ -737,23 +737,13 @@ printk("ptrace: PTRACE_KILL: child:%08lx\n", (unsigned long)child); ...@@ -737,23 +737,13 @@ printk("ptrace: PTRACE_KILL: child:%08lx\n", (unsigned long)child);
} }
/* Compute next pc. */ /* Compute next pc. */
pc = get_stack_long(child, (4 * PT_BPC)); pc = get_stack_long(child, PT_BPC);
#if DEBUG_PTRACE if (access_process_vm(child, pc&~3, &insn, sizeof(insn), 0)
printk("ptrace: PTRACE_SINGLESTEP: child:%08lx, pc:%08lx, ", != sizeof(insn))
(unsigned long)child, pc);
#endif
if (access_process_vm(child, pc&~3, &insn, sizeof(insn), 0) != sizeof(insn))
break; break;
#if DEBUG_PTRACE
printk("(pc&~3):%08lx, insn:%08lx, ", (pc & ~3), insn);
#endif
compute_next_pc(insn, pc, &next_pc, child); compute_next_pc(insn, pc, &next_pc, child);
#if DEBUG_PTRACE
printk("nextpc:%08lx\n", next_pc);
#endif
if (next_pc & 0x80000000) if (next_pc & 0x80000000)
break; break;
...@@ -762,37 +752,77 @@ printk("nextpc:%08lx\n", next_pc); ...@@ -762,37 +752,77 @@ printk("nextpc:%08lx\n", next_pc);
invalidate_cache(); invalidate_cache();
child->exit_code = data; child->exit_code = data;
/* give it a chance to run. */ /* give it a chance to run. */
wake_up_process(child); wake_up_process(child);
ret = 0; ret = 0;
break; break;
} }
case PTRACE_DETACH: /* detach a process that was attached. */ /*
#if DEBUG_PTRACE * detach a process that was attached.
printk("ptrace: PTRACE_DETACH: child:%08lx, data:%08lx\n", */
(unsigned long)child, data); case PTRACE_DETACH:
#endif
ret = 0; ret = 0;
ret = ptrace_detach(child, data); ret = ptrace_detach(child, data);
break; break;
case PTRACE_SETOPTIONS: { case PTRACE_GETREGS:
#if DEBUG_PTRACE ret = ptrace_getregs(child, (void __user *)data);
printk("ptrace: PTRACE_SETOPTIONS:\n"); break;
#endif
if (data & PTRACE_O_TRACESYSGOOD) case PTRACE_SETREGS:
child->ptrace |= PT_TRACESYSGOOD; ret = ptrace_setregs(child, (void __user *)data);
else
child->ptrace &= ~PT_TRACESYSGOOD;
ret = 0;
break; break;
}
default: default:
ret = -EIO; ret = ptrace_request(child, request, addr, data);
break; break;
} }
return ret;
}
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
int ret;
lock_kernel();
ret = -EPERM;
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->ptrace & PT_PTRACED)
goto out;
/* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
ret = 0;
goto out;
}
ret = -ESRCH;
read_lock(&tasklist_lock);
child = find_task_by_pid(pid);
if (child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
goto out;
if (request == PTRACE_ATTACH) {
ret = ptrace_attach(child);
if (ret == 0)
init_debug_traps(child);
goto out_tsk;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL);
if (ret == 0)
ret = do_ptrace(request, child, addr, data);
out_tsk: out_tsk:
put_task_struct(child); put_task_struct(child);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment