diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/x86_64/ia32/ptrace32.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/x86_64/ia32/ptrace32.c')
-rw-r--r-- | arch/x86_64/ia32/ptrace32.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/arch/x86_64/ia32/ptrace32.c b/arch/x86_64/ia32/ptrace32.c new file mode 100644 index 00000000000..b98b6d2462f --- /dev/null +++ b/arch/x86_64/ia32/ptrace32.c @@ -0,0 +1,379 @@ +/* + * 32bit ptrace for x86-64. + * + * Copyright 2001,2002 Andi Kleen, SuSE Labs. + * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier + * copyright. + * + * This allows to access 64bit processes too; but there is no way to see the extended + * register contents. + * + * $Id: ptrace32.c,v 1.16 2003/03/14 16:06:35 ak Exp $ + */ + +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/unistd.h> +#include <linux/mm.h> +#include <linux/ptrace.h> +#include <asm/ptrace.h> +#include <asm/compat.h> +#include <asm/uaccess.h> +#include <asm/user32.h> +#include <asm/user.h> +#include <asm/errno.h> +#include <asm/debugreg.h> +#include <asm/i387.h> +#include <asm/fpu32.h> + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x44dd5UL + +#define R32(l,q) \ + case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break + +static int putreg32(struct task_struct *child, unsigned regno, u32 val) +{ + int i; + __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (regno) { + case offsetof(struct user32, regs.fs): + if (val && (val & 3) != 3) return -EIO; + child->thread.fs = val & 0xffff; + break; + case offsetof(struct user32, regs.gs): + if (val && (val & 3) != 3) return -EIO; + child->thread.gs = val & 0xffff; + break; + case offsetof(struct user32, regs.ds): + if (val && (val & 3) != 3) return -EIO; + child->thread.ds = val & 0xffff; + break; + case offsetof(struct user32, regs.es): + child->thread.es = val & 0xffff; + break; + case offsetof(struct user32, regs.ss): + if ((val & 3) != 3) return -EIO; + stack[offsetof(struct pt_regs, ss)/8] = val & 0xffff; + break; + case offsetof(struct user32, regs.cs): + if ((val & 3) != 3) return -EIO; + stack[offsetof(struct pt_regs, cs)/8] = val & 0xffff; + break; + + R32(ebx, rbx); + R32(ecx, rcx); + R32(edx, rdx); + R32(edi, rdi); + R32(esi, rsi); + R32(ebp, rbp); + R32(eax, rax); + R32(orig_eax, orig_rax); + R32(eip, rip); + R32(esp, rsp); + + case offsetof(struct user32, regs.eflags): { + __u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8]; + val &= FLAG_MASK; + *flags = val | (*flags & ~FLAG_MASK); + break; + } + + case offsetof(struct user32, u_debugreg[4]): + case offsetof(struct user32, u_debugreg[5]): + return -EIO; + + case offsetof(struct user32, u_debugreg[0]): + child->thread.debugreg0 = val; + break; + + case offsetof(struct user32, u_debugreg[1]): + child->thread.debugreg1 = val; + break; + + case offsetof(struct user32, u_debugreg[2]): + child->thread.debugreg2 = val; + break; + + case offsetof(struct user32, u_debugreg[3]): + child->thread.debugreg3 = val; + break; + + case offsetof(struct user32, u_debugreg[6]): + child->thread.debugreg6 = val; + break; + + case offsetof(struct user32, u_debugreg[7]): + val &= ~DR_CONTROL_RESERVED; + /* See arch/i386/kernel/ptrace.c for an explanation of + * this awkward check.*/ + for(i=0; i<4; i++) + if ((0x5454 >> ((val >> (16 + 4*i)) & 0xf)) & 1) + return -EIO; + child->thread.debugreg7 = val; + break; + + default: + if (regno > sizeof(struct user32) || (regno & 3)) + return -EIO; + + /* Other dummy fields in the virtual user structure are ignored */ + break; + } + return 0; +} + +#undef R32 + +#define R32(l,q) \ + case offsetof(struct user32, regs.l): *val = stack[offsetof(struct pt_regs, q)/8]; break + +static int getreg32(struct task_struct *child, unsigned regno, u32 *val) +{ + __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (regno) { + case offsetof(struct user32, regs.fs): + *val = child->thread.fs; + break; + case offsetof(struct user32, regs.gs): + *val = child->thread.gs; + break; + case offsetof(struct user32, regs.ds): + *val = child->thread.ds; + break; + case offsetof(struct user32, regs.es): + *val = child->thread.es; + break; + + R32(cs, cs); + R32(ss, ss); + R32(ebx, rbx); + R32(ecx, rcx); + R32(edx, rdx); + R32(edi, rdi); + R32(esi, rsi); + R32(ebp, rbp); + R32(eax, rax); + R32(orig_eax, orig_rax); + R32(eip, rip); + R32(eflags, eflags); + R32(esp, rsp); + + case offsetof(struct user32, u_debugreg[0]): + *val = child->thread.debugreg0; + break; + case offsetof(struct user32, u_debugreg[1]): + *val = child->thread.debugreg1; + break; + case offsetof(struct user32, u_debugreg[2]): + *val = child->thread.debugreg2; + break; + case offsetof(struct user32, u_debugreg[3]): + *val = child->thread.debugreg3; + break; + case offsetof(struct user32, u_debugreg[6]): + *val = child->thread.debugreg6; + break; + case offsetof(struct user32, u_debugreg[7]): + *val = child->thread.debugreg7; + break; + + default: + if (regno > sizeof(struct user32) || (regno & 3)) + return -EIO; + + /* Other dummy fields in the virtual user structure are ignored */ + *val = 0; + break; + } + return 0; +} + +#undef R32 + +static struct task_struct *find_target(int request, int pid, int *err) +{ + struct task_struct *child; + + *err = -EPERM; + if (pid == 1) + return NULL; + + *err = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (child) { + *err = -EPERM; + if (child->pid == 1) + goto out; + *err = ptrace_check_attach(child, request == PTRACE_KILL); + if (*err < 0) + goto out; + return child; + } + out: + if (child) + put_task_struct(child); + return NULL; + +} + +asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) +{ + struct task_struct *child; + struct pt_regs *childregs; + void __user *datap = compat_ptr(data); + int ret; + __u32 val; + + switch (request) { + default: + return sys_ptrace(request, pid, addr, data); + + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + case PTRACE_POKEUSR: + case PTRACE_PEEKUSR: + case PTRACE_GETREGS: + case PTRACE_SETREGS: + case PTRACE_SETFPREGS: + case PTRACE_GETFPREGS: + case PTRACE_SETFPXREGS: + case PTRACE_GETFPXREGS: + case PTRACE_GETEVENTMSG: + break; + } + + child = find_target(request, pid, &ret); + if (!child) + return ret; + + childregs = (struct pt_regs *)(child->thread.rsp0 - sizeof(struct pt_regs)); + + switch (request) { + case PTRACE_PEEKDATA: + case PTRACE_PEEKTEXT: + ret = 0; + if (access_process_vm(child, addr, &val, sizeof(u32), 0)!=sizeof(u32)) + ret = -EIO; + else + ret = put_user(val, (unsigned int __user *)datap); + break; + + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(u32), 1)!=sizeof(u32)) + ret = -EIO; + break; + + case PTRACE_PEEKUSR: + ret = getreg32(child, addr, &val); + if (ret == 0) + ret = put_user(val, (__u32 __user *)datap); + break; + + case PTRACE_POKEUSR: + ret = putreg32(child, addr, data); + break; + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + int i; + if (!access_ok(VERIFY_WRITE, datap, 16*4)) { + ret = -EIO; + break; + } + ret = 0; + for ( i = 0; i <= 16*4 ; i += sizeof(__u32) ) { + getreg32(child, i, &val); + ret |= __put_user(val,(u32 __user *)datap); + datap += sizeof(u32); + } + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int i; + if (!access_ok(VERIFY_READ, datap, 16*4)) { + ret = -EIO; + break; + } + ret = 0; + for ( i = 0; i <= 16*4; i += sizeof(u32) ) { + ret |= __get_user(tmp, (u32 __user *)datap); + putreg32(child, i, tmp); + datap += sizeof(u32); + } + break; + } + + case PTRACE_GETFPREGS: + ret = -EIO; + if (!access_ok(VERIFY_READ, compat_ptr(data), + sizeof(struct user_i387_struct))) + break; + save_i387_ia32(child, datap, childregs, 1); + ret = 0; + break; + + case PTRACE_SETFPREGS: + ret = -EIO; + if (!access_ok(VERIFY_WRITE, datap, + sizeof(struct user_i387_struct))) + break; + ret = 0; + /* don't check EFAULT to be bug-to-bug compatible to i386 */ + restore_i387_ia32(child, datap, 1); + break; + + case PTRACE_GETFPXREGS: { + struct user32_fxsr_struct __user *u = datap; + init_fpu(child); + ret = -EIO; + if (!access_ok(VERIFY_WRITE, u, sizeof(*u))) + break; + ret = -EFAULT; + if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u))) + break; + ret = __put_user(childregs->cs, &u->fcs); + ret |= __put_user(child->thread.ds, &u->fos); + break; + } + case PTRACE_SETFPXREGS: { + struct user32_fxsr_struct __user *u = datap; + unlazy_fpu(child); + ret = -EIO; + if (!access_ok(VERIFY_READ, u, sizeof(*u))) + break; + /* no checking to be bug-to-bug compatible with i386 */ + __copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u)); + set_stopped_child_used_math(child); + child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; + ret = 0; + break; + } + + case PTRACE_GETEVENTMSG: + ret = put_user(child->ptrace_message,(unsigned int __user *)compat_ptr(data)); + break; + + default: + ret = -EINVAL; + break; + } + + put_task_struct(child); + return ret; +} + |