123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- /* ptrace.c: Sparc process tracing support.
- *
- * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
- *
- * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
- * and David Mosberger.
- *
- * Added Linux support -miguel (weird, eh?, the orignal code was meant
- * to emulate SunOS).
- */
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/errno.h>
- #include <linux/ptrace.h>
- #include <linux/user.h>
- #include <linux/smp.h>
- #include <linux/smp_lock.h>
- #include <linux/security.h>
- #include <linux/signal.h>
- #include <asm/pgtable.h>
- #include <asm/system.h>
- #include <asm/uaccess.h>
- #define MAGIC_CONSTANT 0x80000000
- /* Returning from ptrace is a bit tricky because the syscall return
- * low level code assumes any value returned which is negative and
- * is a valid errno will mean setting the condition codes to indicate
- * an error return. This doesn't work, so we have this hook.
- */
- static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
- {
- regs->u_regs[UREG_I0] = error;
- regs->psr |= PSR_C;
- regs->pc = regs->npc;
- regs->npc += 4;
- }
- static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
- {
- regs->u_regs[UREG_I0] = value;
- regs->psr &= ~PSR_C;
- regs->pc = regs->npc;
- regs->npc += 4;
- }
- static void
- pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr)
- {
- if (put_user(value, addr)) {
- pt_error_return(regs, EFAULT);
- return;
- }
- regs->u_regs[UREG_I0] = 0;
- regs->psr &= ~PSR_C;
- regs->pc = regs->npc;
- regs->npc += 4;
- }
- static void
- pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr)
- {
- if (current->personality == PER_SUNOS)
- pt_succ_return (regs, val);
- else
- pt_succ_return_linux (regs, val, addr);
- }
- /* Fuck me gently with a chainsaw... */
- static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
- struct task_struct *tsk, long __user *addr)
- {
- struct pt_regs *cregs = tsk->thread.kregs;
- struct thread_info *t = tsk->thread_info;
- int v;
-
- if(offset >= 1024)
- offset -= 1024; /* whee... */
- if(offset & ((sizeof(unsigned long) - 1))) {
- pt_error_return(regs, EIO);
- return;
- }
- if(offset >= 16 && offset < 784) {
- offset -= 16; offset >>= 2;
- pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
- return;
- }
- if(offset >= 784 && offset < 832) {
- offset -= 784; offset >>= 2;
- pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
- return;
- }
- switch(offset) {
- case 0:
- v = t->ksp;
- break;
- case 4:
- v = t->kpc;
- break;
- case 8:
- v = t->kpsr;
- break;
- case 12:
- v = t->uwinmask;
- break;
- case 832:
- v = t->w_saved;
- break;
- case 896:
- v = cregs->u_regs[UREG_I0];
- break;
- case 900:
- v = cregs->u_regs[UREG_I1];
- break;
- case 904:
- v = cregs->u_regs[UREG_I2];
- break;
- case 908:
- v = cregs->u_regs[UREG_I3];
- break;
- case 912:
- v = cregs->u_regs[UREG_I4];
- break;
- case 916:
- v = cregs->u_regs[UREG_I5];
- break;
- case 920:
- v = cregs->u_regs[UREG_I6];
- break;
- case 924:
- if(tsk->thread.flags & MAGIC_CONSTANT)
- v = cregs->u_regs[UREG_G1];
- else
- v = 0;
- break;
- case 940:
- v = cregs->u_regs[UREG_I0];
- break;
- case 944:
- v = cregs->u_regs[UREG_I1];
- break;
- case 948:
- /* Isn't binary compatibility _fun_??? */
- if(cregs->psr & PSR_C)
- v = cregs->u_regs[UREG_I0] << 24;
- else
- v = 0;
- break;
- /* Rest of them are completely unsupported. */
- default:
- printk("%s [%d]: Wants to read user offset %ld\n",
- current->comm, current->pid, offset);
- pt_error_return(regs, EIO);
- return;
- }
- if (current->personality == PER_SUNOS)
- pt_succ_return (regs, v);
- else
- pt_succ_return_linux (regs, v, addr);
- return;
- }
- static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset,
- struct task_struct *tsk)
- {
- struct pt_regs *cregs = tsk->thread.kregs;
- struct thread_info *t = tsk->thread_info;
- unsigned long value = regs->u_regs[UREG_I3];
- if(offset >= 1024)
- offset -= 1024; /* whee... */
- if(offset & ((sizeof(unsigned long) - 1)))
- goto failure;
- if(offset >= 16 && offset < 784) {
- offset -= 16; offset >>= 2;
- *(((unsigned long *)(&t->reg_window[0]))+offset) = value;
- goto success;
- }
- if(offset >= 784 && offset < 832) {
- offset -= 784; offset >>= 2;
- *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
- goto success;
- }
- switch(offset) {
- case 896:
- cregs->u_regs[UREG_I0] = value;
- break;
- case 900:
- cregs->u_regs[UREG_I1] = value;
- break;
- case 904:
- cregs->u_regs[UREG_I2] = value;
- break;
- case 908:
- cregs->u_regs[UREG_I3] = value;
- break;
- case 912:
- cregs->u_regs[UREG_I4] = value;
- break;
- case 916:
- cregs->u_regs[UREG_I5] = value;
- break;
- case 920:
- cregs->u_regs[UREG_I6] = value;
- break;
- case 924:
- cregs->u_regs[UREG_I7] = value;
- break;
- case 940:
- cregs->u_regs[UREG_I0] = value;
- break;
- case 944:
- cregs->u_regs[UREG_I1] = value;
- break;
- /* Rest of them are completely unsupported or "no-touch". */
- default:
- printk("%s [%d]: Wants to write user offset %ld\n",
- current->comm, current->pid, offset);
- goto failure;
- }
- success:
- pt_succ_return(regs, 0);
- return;
- failure:
- pt_error_return(regs, EIO);
- return;
- }
- /* #define ALLOW_INIT_TRACING */
- /* #define DEBUG_PTRACE */
- #ifdef DEBUG_PTRACE
- char *pt_rq [] = {
- /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
- /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
- /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
- /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
- /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
- /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
- /* 24 */ "SYSCALL", ""
- };
- #endif
- /*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
- */
- void ptrace_disable(struct task_struct *child)
- {
- /* nothing to do */
- }
- asmlinkage void do_ptrace(struct pt_regs *regs)
- {
- unsigned long request = regs->u_regs[UREG_I0];
- unsigned long pid = regs->u_regs[UREG_I1];
- unsigned long addr = regs->u_regs[UREG_I2];
- unsigned long data = regs->u_regs[UREG_I3];
- unsigned long addr2 = regs->u_regs[UREG_I4];
- struct task_struct *child;
- int ret;
- lock_kernel();
- #ifdef DEBUG_PTRACE
- {
- char *s;
- if ((request >= 0) && (request <= 24))
- s = pt_rq [request];
- else
- s = "unknown";
- if (request == PTRACE_POKEDATA && data == 0x91d02001){
- printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
- pid, addr, addr2);
- } else
- printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
- s, (int) request, (int) pid, addr, data, addr2);
- }
- #endif
- if (request == PTRACE_TRACEME) {
- int my_ret;
- /* are we already being traced? */
- if (current->ptrace & PT_PTRACED) {
- pt_error_return(regs, EPERM);
- goto out;
- }
- my_ret = security_ptrace(current->parent, current);
- if (my_ret) {
- pt_error_return(regs, -my_ret);
- goto out;
- }
- /* set the ptrace bit in the process flags. */
- current->ptrace |= PT_PTRACED;
- pt_succ_return(regs, 0);
- goto out;
- }
- #ifndef ALLOW_INIT_TRACING
- if (pid == 1) {
- /* Can't dork with init. */
- pt_error_return(regs, EPERM);
- goto out;
- }
- #endif
- read_lock(&tasklist_lock);
- child = find_task_by_pid(pid);
- if (child)
- get_task_struct(child);
- read_unlock(&tasklist_lock);
- if (!child) {
- pt_error_return(regs, ESRCH);
- goto out;
- }
- if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
- || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
- if (ptrace_attach(child)) {
- pt_error_return(regs, EPERM);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- ret = ptrace_check_attach(child, request == PTRACE_KILL);
- if (ret < 0) {
- pt_error_return(regs, -ret);
- goto out_tsk;
- }
- switch(request) {
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned long tmp;
- if (access_process_vm(child, addr,
- &tmp, sizeof(tmp), 0) == sizeof(tmp))
- pt_os_succ_return(regs, tmp, (long __user *)data);
- else
- pt_error_return(regs, EIO);
- goto out_tsk;
- }
- case PTRACE_PEEKUSR:
- read_sunos_user(regs, addr, child, (long __user *) data);
- goto out_tsk;
- case PTRACE_POKEUSR:
- write_sunos_user(regs, addr, child);
- goto out_tsk;
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA: {
- if (access_process_vm(child, addr,
- &data, sizeof(data), 1) == sizeof(data))
- pt_succ_return(regs, 0);
- else
- pt_error_return(regs, EIO);
- goto out_tsk;
- }
- case PTRACE_GETREGS: {
- struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
- struct pt_regs *cregs = child->thread.kregs;
- int rval;
- if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) {
- rval = -EFAULT;
- pt_error_return(regs, -rval);
- goto out_tsk;
- }
- __put_user(cregs->psr, (&pregs->psr));
- __put_user(cregs->pc, (&pregs->pc));
- __put_user(cregs->npc, (&pregs->npc));
- __put_user(cregs->y, (&pregs->y));
- for(rval = 1; rval < 16; rval++)
- __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
- pt_succ_return(regs, 0);
- #ifdef DEBUG_PTRACE
- printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
- #endif
- goto out_tsk;
- }
- case PTRACE_SETREGS: {
- struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
- struct pt_regs *cregs = child->thread.kregs;
- unsigned long psr, pc, npc, y;
- int i;
- /* Must be careful, tracing process can only set certain
- * bits in the psr.
- */
- if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) {
- pt_error_return(regs, EFAULT);
- goto out_tsk;
- }
- __get_user(psr, (&pregs->psr));
- __get_user(pc, (&pregs->pc));
- __get_user(npc, (&pregs->npc));
- __get_user(y, (&pregs->y));
- psr &= PSR_ICC;
- cregs->psr &= ~PSR_ICC;
- cregs->psr |= psr;
- if (!((pc | npc) & 3)) {
- cregs->pc = pc;
- cregs->npc =npc;
- }
- cregs->y = y;
- for(i = 1; i < 16; i++)
- __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- case PTRACE_GETFPREGS: {
- struct fps {
- unsigned long regs[32];
- unsigned long fsr;
- unsigned long flags;
- unsigned long extra;
- unsigned long fpqd;
- struct fq {
- unsigned long *insnaddr;
- unsigned long insn;
- } fpq[16];
- };
- struct fps __user *fps = (struct fps __user *) addr;
- int i;
- if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) {
- i = -EFAULT;
- pt_error_return(regs, -i);
- goto out_tsk;
- }
- for(i = 0; i < 32; i++)
- __put_user(child->thread.float_regs[i], (&fps->regs[i]));
- __put_user(child->thread.fsr, (&fps->fsr));
- __put_user(child->thread.fpqdepth, (&fps->fpqd));
- __put_user(0, (&fps->flags));
- __put_user(0, (&fps->extra));
- for(i = 0; i < 16; i++) {
- __put_user(child->thread.fpqueue[i].insn_addr,
- (&fps->fpq[i].insnaddr));
- __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- case PTRACE_SETFPREGS: {
- struct fps {
- unsigned long regs[32];
- unsigned long fsr;
- unsigned long flags;
- unsigned long extra;
- unsigned long fpqd;
- struct fq {
- unsigned long *insnaddr;
- unsigned long insn;
- } fpq[16];
- };
- struct fps __user *fps = (struct fps __user *) addr;
- int i;
- if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) {
- i = -EFAULT;
- pt_error_return(regs, -i);
- goto out_tsk;
- }
- copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
- __get_user(child->thread.fsr, (&fps->fsr));
- __get_user(child->thread.fpqdepth, (&fps->fpqd));
- for(i = 0; i < 16; i++) {
- __get_user(child->thread.fpqueue[i].insn_addr,
- (&fps->fpq[i].insnaddr));
- __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- case PTRACE_READTEXT:
- case PTRACE_READDATA: {
- int res = ptrace_readdata(child, addr,
- (void __user *) addr2, data);
- if (res == data) {
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- /* Partial read is an IO failure */
- if (res >= 0)
- res = -EIO;
- pt_error_return(regs, -res);
- goto out_tsk;
- }
- case PTRACE_WRITETEXT:
- case PTRACE_WRITEDATA: {
- int res = ptrace_writedata(child, (void __user *) addr2,
- addr, data);
- if (res == data) {
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- /* Partial write is an IO failure */
- if (res >= 0)
- res = -EIO;
- pt_error_return(regs, -res);
- goto out_tsk;
- }
- case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
- addr = 1;
- case PTRACE_CONT: { /* restart after signal. */
- if (!valid_signal(data)) {
- pt_error_return(regs, EIO);
- goto out_tsk;
- }
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- child->exit_code = data;
- #ifdef DEBUG_PTRACE
- printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n",
- child->comm, child->pid, child->exit_code,
- child->thread.kregs->pc,
- child->thread.kregs->npc);
- #endif
- wake_up_process(child);
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- /*
- * 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->exit_state == EXIT_ZOMBIE) { /* already dead */
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- wake_up_process(child);
- child->exit_code = SIGKILL;
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- case PTRACE_SUNDETACH: { /* detach a process that was attached. */
- int err = ptrace_detach(child, data);
- if (err) {
- pt_error_return(regs, EIO);
- goto out_tsk;
- }
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- /* PTRACE_DUMPCORE unsupported... */
- default: {
- int err = ptrace_request(child, request, addr, data);
- if (err)
- pt_error_return(regs, -err);
- else
- pt_succ_return(regs, 0);
- goto out_tsk;
- }
- }
- out_tsk:
- if (child)
- put_task_struct(child);
- out:
- unlock_kernel();
- }
- asmlinkage void syscall_trace(void)
- {
- #ifdef DEBUG_PTRACE
- printk("%s [%d]: syscall_trace\n", current->comm, current->pid);
- #endif
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
- return;
- if (!(current->ptrace & PT_PTRACED))
- return;
- current->thread.flags ^= MAGIC_CONSTANT;
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- #ifdef DEBUG_PTRACE
- printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
- current->pid, current->exit_code);
- #endif
- if (current->exit_code) {
- send_sig (current->exit_code, current, 1);
- current->exit_code = 0;
- }
- }
|