Index: sys/amd64/linux/linux_dummy.c =================================================================== --- sys/amd64/linux/linux_dummy.c +++ sys/amd64/linux/linux_dummy.c @@ -45,7 +45,6 @@ DUMMY(mincore); DUMMY(sendfile); -DUMMY(ptrace); DUMMY(syslog); DUMMY(setfsuid); DUMMY(setfsgid); Index: sys/amd64/linux/linux_ptrace.c =================================================================== --- sys/amd64/linux/linux_ptrace.c +++ sys/amd64/linux/linux_ptrace.c @@ -1,483 +1,417 @@ /*- - * Copyright (c) 2001 Alexander Kabaev + * Copyright (c) 2017 Edward Tomasz Napierala * All rights reserved. * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) + * ("CTSRD"), as part of the DARPA CRASH research programme. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer - * in this position and unchanged. + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include -__FBSDID("$FreeBSD: head/sys/i386/linux/linux_ptrace.c 302921 2016-07-16 00:54:46Z jhb $"); - -#include "opt_cpu.h" +__FBSDID("$FreeBSD$"); #include -#include -#include #include #include #include -#include - -#include #include #include - -#include -#include +#include +#include +#include #include -#if !defined(CPU_DISABLE_SSE) && defined(I686_CPU) -#define CPU_ENABLE_SSE -#endif +#define LINUX_PTRACE_TRACEME 0 +#define LINUX_PTRACE_PEEKTEXT 1 +#define LINUX_PTRACE_PEEKDATA 2 +#define LINUX_PTRACE_PEEKUSER 3 +#define LINUX_PTRACE_POKETEXT 4 +#define LINUX_PTRACE_POKEDATA 5 +#define LINUX_PTRACE_POKEUSER 6 +#define LINUX_PTRACE_CONT 7 +#define LINUX_PTRACE_KILL 8 +#define LINUX_PTRACE_SINGLESTEP 9 +#define LINUX_PTRACE_GETREGS 12 +#define LINUX_PTRACE_SETREGS 13 +#define LINUX_PTRACE_GETFPREGS 14 +#define LINUX_PTRACE_SETFPREGS 15 +#define LINUX_PTRACE_ATTACH 16 +#define LINUX_PTRACE_DETACH 17 +#define LINUX_PTRACE_SYSCALL 24 +#define LINUX_PTRACE_SETOPTIONS 0x4200 +#define LINUX_PTRACE_GETREGSET 0x4204 +#define LINUX_PTRACE_SEIZE 0x4206 + +#define LINUX_PTRACE_O_TRACESYSGOOD 1 +#define LINUX_PTRACE_O_TRACEFORK 2 +#define LINUX_PTRACE_O_TRACEVFORK 4 +#define LINUX_PTRACE_O_TRACECLONE 8 +#define LINUX_PTRACE_O_TRACEEXEC 16 +#define LINUX_PTRACE_O_TRACEVFORKDONE 32 +#define LINUX_PTRACE_O_TRACEEXIT 64 +#define LINUX_PTRACE_O_TRACESECCOMP 128 +#define LINUX_PTRACE_O_EXITKILL 1048576 +#define LINUX_PTRACE_O_SUSPEND_SECCOMP 2097152 + +#define LINUX_NT_PRSTATUS 1 + +#define LINUX_PTRACE_O_MASK (LINUX_PTRACE_O_TRACESYSGOOD | \ + LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK | \ + LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC | \ + LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT | \ + LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL | \ + LINUX_PTRACE_O_SUSPEND_SECCOMP) -/* - * Linux ptrace requests numbers. Mostly identical to FreeBSD, - * except for MD ones and PT_ATTACH/PT_DETACH. - */ -#define PTRACE_TRACEME 0 -#define PTRACE_PEEKTEXT 1 -#define PTRACE_PEEKDATA 2 -#define PTRACE_PEEKUSR 3 -#define PTRACE_POKETEXT 4 -#define PTRACE_POKEDATA 5 -#define PTRACE_POKEUSR 6 -#define PTRACE_CONT 7 -#define PTRACE_KILL 8 -#define PTRACE_SINGLESTEP 9 - -#define PTRACE_ATTACH 16 -#define PTRACE_DETACH 17 - -#define LINUX_PTRACE_SYSCALL 24 - -#define PTRACE_GETREGS 12 -#define PTRACE_SETREGS 13 -#define PTRACE_GETFPREGS 14 -#define PTRACE_SETFPREGS 15 -#define PTRACE_GETFPXREGS 18 -#define PTRACE_SETFPXREGS 19 +struct linux_pt_reg { + l_ulong r15; + l_ulong r14; + l_ulong r13; + l_ulong r12; + l_ulong rbp; + l_ulong rbx; + l_ulong r11; + l_ulong r10; + l_ulong r9; + l_ulong r8; + l_ulong rax; + l_ulong rcx; + l_ulong rdx; + l_ulong rsi; + l_ulong rdi; + l_ulong orig_rax; + l_ulong rip; + l_ulong cs; + l_ulong eflags; + l_ulong rsp; + l_ulong ss; +}; -#define PTRACE_SETOPTIONS 21 +static int +map_signum(int lsig, int *bsigp) +{ + int bsig; -/* - * Linux keeps debug registers at the following - * offset in the user struct - */ -#define LINUX_DBREG_OFFSET 252 -#define LINUX_DBREG_SIZE (8*sizeof(l_int)) + if (lsig == 0) { + *bsigp = 0; + return (0); + } -static __inline int -map_signum(int signum) -{ + if (lsig < 0 || lsig > LINUX_SIGRTMAX) + return (EINVAL); - signum = linux_to_bsd_signal(signum); - return ((signum == SIGSTOP)? 0 : signum); -} + bsig = linux_to_bsd_signal(lsig); + if (bsig == SIGSTOP) + bsig = 0; -struct linux_pt_reg { - l_long ebx; - l_long ecx; - l_long edx; - l_long esi; - l_long edi; - l_long ebp; - l_long eax; - l_int xds; - l_int xes; - l_int xfs; - l_int xgs; - l_long orig_eax; - l_long eip; - l_int xcs; - l_long eflags; - l_long esp; - l_int xss; -}; + *bsigp = bsig; + return (0); +} /* - * Translate i386 ptrace registers between Linux and FreeBSD formats. - * The translation is pretty straighforward, for all registers, but - * orig_eax on Linux side and r_trapno and r_err in FreeBSD + * Translate amd64 ptrace registers between Linux and FreeBSD formats. + * The translation is pretty straighforward, for all registers but + * orig_rax on Linux side and r_trapno and r_err in FreeBSD. */ static void -map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) +map_regs_to_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) { - linux_r->ebx = bsd_r->r_ebx; - linux_r->ecx = bsd_r->r_ecx; - linux_r->edx = bsd_r->r_edx; - linux_r->esi = bsd_r->r_esi; - linux_r->edi = bsd_r->r_edi; - linux_r->ebp = bsd_r->r_ebp; - linux_r->eax = bsd_r->r_eax; - linux_r->xds = bsd_r->r_ds; - linux_r->xes = bsd_r->r_es; - linux_r->xfs = bsd_r->r_fs; - linux_r->xgs = bsd_r->r_gs; - linux_r->orig_eax = bsd_r->r_eax; - linux_r->eip = bsd_r->r_eip; - linux_r->xcs = bsd_r->r_cs; - linux_r->eflags = bsd_r->r_eflags; - linux_r->esp = bsd_r->r_esp; - linux_r->xss = bsd_r->r_ss; + + l_reg->r15 = b_reg->r_r15; + l_reg->r14 = b_reg->r_r14; + l_reg->r13 = b_reg->r_r13; + l_reg->r12 = b_reg->r_r12; + l_reg->rbp = b_reg->r_rbp; + l_reg->rbx = b_reg->r_rbx; + l_reg->r11 = b_reg->r_r11; + l_reg->r10 = b_reg->r_r10; + l_reg->r9 = b_reg->r_r9; + l_reg->r8 = b_reg->r_r8; + l_reg->rax = b_reg->r_rax; + l_reg->rcx = b_reg->r_rcx; + l_reg->rdx = b_reg->r_rdx; + l_reg->rsi = b_reg->r_rsi; + l_reg->rdi = b_reg->r_rdi; + l_reg->orig_rax = b_reg->r_rax; + l_reg->rip = b_reg->r_rip; + l_reg->cs = b_reg->r_cs; + l_reg->eflags = b_reg->r_rflags; + l_reg->rsp = b_reg->r_rsp; + l_reg->ss = b_reg->r_ss; } static void -map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) +map_regs_from_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) { - bsd_r->r_ebx = linux_r->ebx; - bsd_r->r_ecx = linux_r->ecx; - bsd_r->r_edx = linux_r->edx; - bsd_r->r_esi = linux_r->esi; - bsd_r->r_edi = linux_r->edi; - bsd_r->r_ebp = linux_r->ebp; - bsd_r->r_eax = linux_r->eax; - bsd_r->r_ds = linux_r->xds; - bsd_r->r_es = linux_r->xes; - bsd_r->r_fs = linux_r->xfs; - bsd_r->r_gs = linux_r->xgs; - bsd_r->r_eip = linux_r->eip; - bsd_r->r_cs = linux_r->xcs; - bsd_r->r_eflags = linux_r->eflags; - bsd_r->r_esp = linux_r->esp; - bsd_r->r_ss = linux_r->xss; + b_reg->r_r15 = l_reg->r15; + b_reg->r_r14 = l_reg->r14; + b_reg->r_r13 = l_reg->r13; + b_reg->r_r12 = l_reg->r12; + b_reg->r_r11 = l_reg->r11; + b_reg->r_r10 = l_reg->r10; + b_reg->r_r9 = l_reg->r9; + b_reg->r_r8 = l_reg->r8; + b_reg->r_rdi = l_reg->rdi; + b_reg->r_rsi = l_reg->rsi; + b_reg->r_rbp = l_reg->rbp; + b_reg->r_rbx = l_reg->rbx; + b_reg->r_rdx = l_reg->rdx; + b_reg->r_rcx = l_reg->rcx; + b_reg->r_rax = l_reg->rax; + + /* + * XXX: Are zeroes the right thing to put here? + */ + b_reg->r_trapno = 0; + b_reg->r_fs = 0; + b_reg->r_gs = 0; + b_reg->r_err = 0; + b_reg->r_es = 0; + b_reg->r_ds = 0; + + b_reg->r_rip = l_reg->rip; + b_reg->r_cs = l_reg->cs; + b_reg->r_rflags = l_reg->eflags; + b_reg->r_rsp = l_reg->rsp; + b_reg->r_ss = l_reg->ss; } -struct linux_pt_fpreg { - l_long cwd; - l_long swd; - l_long twd; - l_long fip; - l_long fcs; - l_long foo; - l_long fos; - l_long st_space[2*10]; -}; +static int +linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data) +{ + int error; -static void -map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) + error = kern_ptrace(td, PT_READ_I, pid, addr, 0); + if (error == 0) + error = copyout(td->td_retval, data, sizeof(l_int)); + td->td_retval[0] = error; + + return (error); +} + +static int +linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data) { - linux_r->cwd = bsd_r->fpr_env[0]; - linux_r->swd = bsd_r->fpr_env[1]; - linux_r->twd = bsd_r->fpr_env[2]; - linux_r->fip = bsd_r->fpr_env[3]; - linux_r->fcs = bsd_r->fpr_env[4]; - linux_r->foo = bsd_r->fpr_env[5]; - linux_r->fos = bsd_r->fpr_env[6]; - bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space)); + int mask; + + mask = 0; + + if (data & ~LINUX_PTRACE_O_MASK) { + printf("%s: unknown ptrace option %lx set; " + "returning EINVAL\n", + __func__, data & ~LINUX_PTRACE_O_MASK); + return (EINVAL); + } + + /* + * PTRACE_O_EXITKILL is ignored, we do that by default. + */ + + if (data & LINUX_PTRACE_O_TRACESYSGOOD) { + printf("%s: PTRACE_O_TRACESYSGOOD not implemented; " + "returning EINVAL\n", __func__); + return (EINVAL); + } + + if (data & LINUX_PTRACE_O_TRACEFORK) + mask |= PTRACE_FORK; + + if (data & LINUX_PTRACE_O_TRACEVFORK) + mask |= PTRACE_VFORK; + + if (data & LINUX_PTRACE_O_TRACECLONE) + mask |= PTRACE_VFORK; + + if (data & LINUX_PTRACE_O_TRACEEXEC) + mask |= PTRACE_EXEC; + + if (data & LINUX_PTRACE_O_TRACEVFORKDONE) + mask |= PTRACE_VFORK; /* XXX: Close enough? */ + + if (data & LINUX_PTRACE_O_TRACEEXIT) { + printf("%s: PTRACE_O_TRACESYSGOOD not implemented; " + "returning EINVAL\n", __func__); + return (EINVAL); + } + + return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask))); } -static void -map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) +static int +linux_ptrace_getregs(struct thread *td, pid_t pid, void *data) { - bsd_r->fpr_env[0] = linux_r->cwd; - bsd_r->fpr_env[1] = linux_r->swd; - bsd_r->fpr_env[2] = linux_r->twd; - bsd_r->fpr_env[3] = linux_r->fip; - bsd_r->fpr_env[4] = linux_r->fcs; - bsd_r->fpr_env[5] = linux_r->foo; - bsd_r->fpr_env[6] = linux_r->fos; - bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc)); + struct ptrace_lwpinfo lwpinfo; + struct reg b_reg; + struct linux_pt_reg l_reg; + int error; + + error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); + if (error != 0) + return (error); + + map_regs_to_linux(&b_reg, &l_reg); + + /* + * The strace(1) utility depends on RAX being set to -ENOSYS + * on syscall entry. + */ + error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); + if (error != 0) { + printf("%s: PT_LWPINFO failed with error %d\n", + __func__, error); + return (error); + } + if (lwpinfo.pl_flags & PL_FLAG_SCE) + l_reg.rax = -38; // XXX: This should be #defined somewhere. + + error = copyout(&l_reg, (void *)data, sizeof(l_reg)); + return (error); } -struct linux_pt_fpxreg { - l_ushort cwd; - l_ushort swd; - l_ushort twd; - l_ushort fop; - l_long fip; - l_long fcs; - l_long foo; - l_long fos; - l_long mxcsr; - l_long reserved; - l_long st_space[32]; - l_long xmm_space[32]; - l_long padding[56]; -}; +static int +linux_ptrace_setregs(struct thread *td, pid_t pid, void *data) +{ + struct reg b_reg; + struct linux_pt_reg l_reg; + int error; + + error = copyin(data, &l_reg, sizeof(l_reg)); + if (error != 0) + return (error); + map_regs_from_linux(&b_reg, &l_reg); + error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0); + return (error); +} -#ifdef CPU_ENABLE_SSE static int -linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) +linux_ptrace_getregset(struct thread *td, pid_t pid, + l_ulong addr, l_ulong data) { - PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); - if (cpu_fxsr == 0 || (td->td_proc->p_flag & P_INMEM) == 0) - return (EIO); - bcopy(&get_pcb_user_save_td(td)->sv_xmm, fpxregs, sizeof(*fpxregs)); - return (0); + switch (addr) { + case LINUX_NT_PRSTATUS: + printf("%s: NT_PRSTATUS not implemented; returning EINVAL\n", + __func__); + return (EINVAL); + default: + printf("%s: PTRACE_GETREGSET request %ld not implemented; " + "returning EINVAL\n", __func__, addr); + return (EINVAL); + } } static int -linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) +linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { - PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); - if (cpu_fxsr == 0 || (td->td_proc->p_flag & P_INMEM) == 0) - return (EIO); - bcopy(fpxregs, &get_pcb_user_save_td(td)->sv_xmm, sizeof(*fpxregs)); - return (0); + printf("%s: PTRACE_SEIZE not implemented; returning EINVAL\n", + __func__); + return (EINVAL); } -#endif int linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) { - union { - struct linux_pt_reg reg; - struct linux_pt_fpreg fpreg; - struct linux_pt_fpxreg fpxreg; - } r; - union { - struct reg bsd_reg; - struct fpreg bsd_fpreg; - struct dbreg bsd_dbreg; - } u; void *addr; pid_t pid; - int error, req; + int error, sig; - error = 0; + AUDIT_ARG_PID(uap->pid); + AUDIT_ARG_CMD(uap->req); + AUDIT_ARG_VALUE(uap->data); - /* by default, just copy data intact */ - req = uap->req; pid = (pid_t)uap->pid; addr = (void *)uap->addr; - switch (req) { - case PTRACE_TRACEME: - case PTRACE_POKETEXT: - case PTRACE_POKEDATA: - case PTRACE_KILL: - error = kern_ptrace(td, req, pid, addr, uap->data); - break; - case PTRACE_PEEKTEXT: - case PTRACE_PEEKDATA: { - /* need to preserve return value */ - int rval = td->td_retval[0]; - error = kern_ptrace(td, req, pid, addr, 0); - if (error == 0) - error = copyout(td->td_retval, (void *)uap->data, - sizeof(l_int)); - td->td_retval[0] = rval; + switch (uap->req) { + case LINUX_PTRACE_TRACEME: + error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0); + break; + case LINUX_PTRACE_PEEKTEXT: + case LINUX_PTRACE_PEEKDATA: + error = linux_ptrace_peek(td, pid, addr, (void *)uap->data); + if (error != 0) + return (error); + /* + * Linux expects this syscall to read 64 bits, not 32. + */ + error = linux_ptrace_peek(td, pid, + (void *)(uap->addr + 4), (void *)(uap->data + 4)); break; - } - case PTRACE_DETACH: - error = kern_ptrace(td, PT_DETACH, pid, (void *)1, - map_signum(uap->data)); - break; - case PTRACE_SINGLESTEP: - case PTRACE_CONT: - error = kern_ptrace(td, req, pid, (void *)1, - map_signum(uap->data)); + case LINUX_PTRACE_POKETEXT: + error = kern_ptrace(td, PT_WRITE_I, pid, addr, uap->data); break; - case PTRACE_ATTACH: - error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); + case LINUX_PTRACE_POKEDATA: + error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data); break; - case PTRACE_GETREGS: - /* Linux is using data where FreeBSD is using addr */ - error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0); - if (error == 0) { - map_regs_to_linux(&u.bsd_reg, &r.reg); - error = copyout(&r.reg, (void *)uap->data, - sizeof(r.reg)); - } - break; - case PTRACE_SETREGS: - /* Linux is using data where FreeBSD is using addr */ - error = copyin((void *)uap->data, &r.reg, sizeof(r.reg)); - if (error == 0) { - map_regs_from_linux(&u.bsd_reg, &r.reg); - error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0); - } - break; - case PTRACE_GETFPREGS: - /* Linux is using data where FreeBSD is using addr */ - error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0); - if (error == 0) { - map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg); - error = copyout(&r.fpreg, (void *)uap->data, - sizeof(r.fpreg)); - } - break; - case PTRACE_SETFPREGS: - /* Linux is using data where FreeBSD is using addr */ - error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg)); - if (error == 0) { - map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg); - error = kern_ptrace(td, PT_SETFPREGS, pid, - &u.bsd_fpreg, 0); - } - break; - case PTRACE_SETFPXREGS: -#ifdef CPU_ENABLE_SSE - error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg)); - if (error) - break; -#endif - /* FALL THROUGH */ - case PTRACE_GETFPXREGS: { -#ifdef CPU_ENABLE_SSE - struct proc *p; - struct thread *td2; - - if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) { - static int once = 0; - if (!once) { - printf("linux: savexmm != linux_pt_fpxreg\n"); - once = 1; - } - error = EIO; + case LINUX_PTRACE_CONT: + error = map_signum(uap->data, &sig); + if (error != 0) break; - } - - if ((p = pfind(uap->pid)) == NULL) { - error = ESRCH; + error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig); + break; + case LINUX_PTRACE_KILL: + error = kern_ptrace(td, PT_KILL, pid, addr, uap->data); + break; + case LINUX_PTRACE_SINGLESTEP: + error = map_signum(uap->data, &sig); + if (error != 0) break; - } - - /* Exiting processes can't be debugged. */ - if ((p->p_flag & P_WEXIT) != 0) { - error = ESRCH; - goto fail; - } - - if ((error = p_candebug(td, p)) != 0) - goto fail; - - /* System processes can't be debugged. */ - if ((p->p_flag & P_SYSTEM) != 0) { - error = EINVAL; - goto fail; - } - - /* not being traced... */ - if ((p->p_flag & P_TRACED) == 0) { - error = EPERM; - goto fail; - } - - /* not being traced by YOU */ - if (p->p_pptr != td->td_proc) { - error = EBUSY; - goto fail; - } - - /* not currently stopped */ - if (!P_SHOULDSTOP(p) || (p->p_flag & P_WAITED) == 0) { - error = EBUSY; - goto fail; - } - - if (req == PTRACE_GETFPXREGS) { - _PHOLD(p); /* may block */ - td2 = FIRST_THREAD_IN_PROC(p); - error = linux_proc_read_fpxregs(td2, &r.fpxreg); - _PRELE(p); - PROC_UNLOCK(p); - if (error == 0) - error = copyout(&r.fpxreg, (void *)uap->data, - sizeof(r.fpxreg)); - } else { - /* clear dangerous bits exactly as Linux does*/ - r.fpxreg.mxcsr &= 0xffbf; - _PHOLD(p); /* may block */ - td2 = FIRST_THREAD_IN_PROC(p); - error = linux_proc_write_fpxregs(td2, &r.fpxreg); - _PRELE(p); - PROC_UNLOCK(p); - } - break; - - fail: - PROC_UNLOCK(p); -#else - error = EIO; -#endif + error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig); break; - } - case PTRACE_PEEKUSR: - case PTRACE_POKEUSR: { - error = EIO; - - /* check addr for alignment */ - if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1)) + case LINUX_PTRACE_GETREGS: + error = linux_ptrace_getregs(td, pid, (void *)uap->data); + break; + case LINUX_PTRACE_SETREGS: + error = linux_ptrace_setregs(td, pid, (void *)uap->data); + break; + case LINUX_PTRACE_ATTACH: + error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); + break; + case LINUX_PTRACE_DETACH: + error = map_signum(uap->data, &sig); + if (error != 0) break; - /* - * Allow linux programs to access register values in - * user struct. We simulate this through PT_GET/SETREGS - * as necessary. - */ - if (uap->addr < sizeof(struct linux_pt_reg)) { - error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0); - if (error != 0) - break; - - map_regs_to_linux(&u.bsd_reg, &r.reg); - if (req == PTRACE_PEEKUSR) { - error = copyout((char *)&r.reg + uap->addr, - (void *)uap->data, sizeof(l_int)); - break; - } - - *(l_int *)((char *)&r.reg + uap->addr) = - (l_int)uap->data; - - map_regs_from_linux(&u.bsd_reg, &r.reg); - error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0); - } - - /* - * Simulate debug registers access - */ - if (uap->addr >= LINUX_DBREG_OFFSET && - uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) { - error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg, - 0); - if (error != 0) - break; - - uap->addr -= LINUX_DBREG_OFFSET; - if (req == PTRACE_PEEKUSR) { - error = copyout((char *)&u.bsd_dbreg + - uap->addr, (void *)uap->data, - sizeof(l_int)); - break; - } - - *(l_int *)((char *)&u.bsd_dbreg + uap->addr) = - uap->data; - error = kern_ptrace(td, PT_SETDBREGS, pid, - &u.bsd_dbreg, 0); - } - + error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig); break; - } case LINUX_PTRACE_SYSCALL: - /* fall through */ + error = map_signum(uap->data, &sig); + if (error != 0) + break; + error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig); + break; + case LINUX_PTRACE_SETOPTIONS: + error = linux_ptrace_setoptions(td, pid, uap->data); + break; + case LINUX_PTRACE_GETREGSET: + error = linux_ptrace_getregset(td, pid, uap->addr, uap->data); + break; + case LINUX_PTRACE_SEIZE: + error = linux_ptrace_seize(td, pid, uap->addr, uap->data); + break; default: - printf("linux: ptrace(%u, ...) not implemented\n", - (unsigned int)uap->req); + printf("%s: ptrace(%ld, ...) not implemented; " + "returning EINVAL\n", __func__, uap->req); error = EINVAL; break; } Index: sys/amd64/linux/syscalls.master =================================================================== --- sys/amd64/linux/syscalls.master +++ sys/amd64/linux/syscalls.master @@ -219,7 +219,7 @@ 99 AUE_NULL STD { int linux_sysinfo(struct l_sysinfo *info); } 100 AUE_NULL STD { int linux_times(struct l_times_argv *buf); } 101 AUE_PTRACE STD { int linux_ptrace(l_long req, l_long pid, \ - l_long addr, l_long data); } + l_ulong addr, l_ulong data); } 102 AUE_GETUID STD { int linux_getuid(void); } 103 AUE_NULL STD { int linux_syslog(l_int type, char *buf, \ l_int len); } Index: sys/modules/linux64/Makefile =================================================================== --- sys/modules/linux64/Makefile +++ sys/modules/linux64/Makefile @@ -7,7 +7,7 @@ KMOD= linux64 SRCS= linux_fork.c linux_dummy.c linux_file.c linux_event.c \ linux_futex.c linux_getcwd.c linux_ioctl.c linux_ipc.c \ - linux_machdep.c linux_misc.c linux_signal.c \ + linux_machdep.c linux_misc.c linux_ptrace.c linux_signal.c \ linux_socket.c linux_stats.c linux_sysctl.c linux_sysent.c \ linux_sysvec.c linux_time.c linux_vdso.c linux_timer.c \ opt_inet6.h opt_compat.h opt_posix.h opt_usb.h \