From owner-freebsd-bugs Mon May 13 21:43:53 2002 Delivered-To: freebsd-bugs@freebsd.org Received: from h132-197-179-27.gte.com (h132-197-179-27.gte.com [132.197.179.27]) by hub.freebsd.org (Postfix) with ESMTP id 0675137B401; Mon, 13 May 2002 21:43:29 -0700 (PDT) Received: from kanpc.gte.com (localhost [IPv6:::1]) by h132-197-179-27.gte.com (8.12.3/8.12.3) with ESMTP id g4E4etfS008156; Tue, 14 May 2002 00:40:55 -0400 (EDT) (envelope-from ak03@kanpc.gte.com) Received: (from ak03@localhost) by kanpc.gte.com (8.12.3/8.12.3/Submit) id g4E4eoR5008155; Tue, 14 May 2002 00:40:50 -0400 (EDT) Date: Tue, 14 May 2002 00:40:50 -0400 From: "Alexander N. Kabaev" To: gnats-admin@FreeBSD.org, freebsd-bugs@FreeBSD.org Cc: Marcel Moolenaar , Dag-Erling Smorgrav Subject: Re: i386/33299: -CURRENT ptrace(2) implementation for linux emulator on i386 Message-ID: <20020514044050.GA8057@kanpc.gte.com> References: <200112291621.fBTGLUW50719@h132-197-179-27.gte.com> <200112291630.fBTGU0H89078@freefall.freebsd.org> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <200112291630.fBTGU0H89078@freefall.freebsd.org> User-Agent: Mutt/1.3.99i Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org Below is an updated patch for ptrace(2) Linux emulation. It compiles and seems to work OK on pre-GCC3 -CURRENT. The change in sys_process.c is not in fact Linux-specific and probably deserves a PR on its own. Index: conf/files.i386 =================================================================== RCS file: /home/ncvs/src/sys/conf/files.i386,v retrieving revision 1.398 diff -u -r1.398 files.i386 --- conf/files.i386 31 Mar 2002 22:28:28 -0000 1.398 +++ conf/files.i386 3 Apr 2002 00:47:57 -0000 @@ -303,6 +303,7 @@ i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_machdep.c optional compat_linux +i386/linux/linux_ptrace.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/pci/pci_cfgreg.c optional pci Index: kern/sys_process.c =================================================================== RCS file: /home/ncvs/src/sys/kern/sys_process.c,v retrieving revision 1.91 diff -u -r1.91 sys_process.c --- kern/sys_process.c 20 Apr 2002 21:56:42 -0000 1.91 +++ kern/sys_process.c 14 May 2002 03:39:16 -0000 @@ -516,7 +516,7 @@ case PT_CONTINUE: case PT_DETACH: /* XXX uap->data is used even in the PT_STEP case. */ - if ((uap->req != PT_STEP) && ((unsigned)uap->data >= NSIG)) { + if (uap->req != PT_STEP && (unsigned)uap->data > _SIG_MAXSIG) { error = EINVAL; goto fail; } Index: i386/linux/linux_ptrace.c =================================================================== RCS file: i386/linux/linux_ptrace.c diff -N i386/linux/linux_ptrace.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ i386/linux/linux_ptrace.c 14 May 2002 03:56:53 -0000 @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2001 Alexander Kabaev + * All rights reserved. + * + * 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. + * 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. + * + * $FreeBSD:$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* + * 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 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 + +#define PTRACE_SETOPTIONS 21 + +/* + * 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) + +static __inline__ int +map_signum(int signum) +{ +#ifndef __alpha__ + if (signum > 0 && signum <= LINUX_SIGTBLSZ) + signum = linux_to_bsd_signal[_SIG_IDX(signum)]; +#endif + return ((signum == SIGSTOP)? 0 : signum); +} + +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; +}; + + +/* + * 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 + */ +static int +map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) +{ + 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; + return (0); +} + +static int +map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) +{ + 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; + return (0); +} + +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 +map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) +{ + 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)); + return (0); +} + +static int +map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) +{ + 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)); + return (0); +} + +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_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) +{ +#ifdef CPU_ENABLE_SSE + if (sizeof(*fpxregs) != sizeof(td->td_pcb->pcb_save.sv_xmm)) { + printf("linux: savexmm != linux_pt_fpxreg\n"); + return (EIO); + } + if (cpu_fxsr == 0) +#endif + return (EIO); + mtx_lock_spin(&sched_lock); + bcopy(&td->td_pcb->pcb_save.sv_xmm, fpxregs, sizeof *fpxregs); + mtx_unlock_spin(&sched_lock); + return (0); +} + +static int +linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) +{ +#ifdef CPU_ENABLE_SSE + if (sizeof(*fpxregs) != sizeof(td->td_pcb->pcb_save.sv_xmm)) { + printf("linux: savexmm != linux_pt_fpxreg\n"); + return (EIO); + } + if (cpu_fxsr == 0) +#endif + return (EIO); + mtx_lock_spin(&sched_lock); + if ((td->td_proc->p_sflag & PS_INMEM) == 0) { + mtx_unlock_spin(&sched_lock); + return (EIO); + } + bcopy(fpxregs, &td->td_pcb->pcb_save.sv_xmm, sizeof *fpxregs); + mtx_unlock_spin(&sched_lock); + return (0); +} + + +int +linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) +{ + struct ptrace_args bsd_args; + int error; + caddr_t sg; + union { + struct linux_pt_reg reg; + struct linux_pt_fpreg fpreg; + struct linux_pt_fpxreg fpxreg; + } r; + + sg = stackgap_init(); + + error = 0; + + /* by default, just copy data intact */ + bsd_args.req = uap->req; + bsd_args.pid = (pid_t)uap->pid; + bsd_args.addr = (caddr_t)uap->addr; + bsd_args.data = uap->data; + + switch (uap->req) { + case PTRACE_TRACEME: + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + case PTRACE_KILL: + error = ptrace(td, &bsd_args); + break; + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: { + /* need to preserve return value */ + int rval = td->td_retval[0]; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + if (error == 0) + error = copyout(td->td_retval, + (caddr_t)uap->data, sizeof(l_int)); + td->td_retval[0] = rval; + break; + } + case PTRACE_DETACH: + bsd_args.req = PT_DETACH; + /* fall through */ + case PTRACE_SINGLESTEP: + case PTRACE_CONT: + bsd_args.data = map_signum(uap->data); + bsd_args.addr = (caddr_t)1; + error = ptrace(td, &bsd_args); + break; + case PTRACE_ATTACH: + bsd_args.req = PT_ATTACH; + error = ptrace(td, &bsd_args); + break; + case PTRACE_GETREGS: { + struct reg *bsd_r = (struct reg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + /* Linux is using data where FreeBSD is using addr */ + bsd_args.req = PT_GETREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + if (error == 0) + error = map_regs_to_linux(bsd_r, &r.reg); + if (error == 0) + error = copyout(&r.reg, (caddr_t)uap->data, sizeof r.reg); + break; + } + case PTRACE_SETREGS: { + struct reg *bsd_r = (struct reg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + /* Linux is using data where FreeBSD is using addr */ + bsd_args.req = PT_SETREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = copyin((caddr_t)uap->data, &r.reg, sizeof r.reg); + if (error == 0) + error = map_regs_from_linux(bsd_r, &r.reg); + if (error == 0) + error = ptrace(td, &bsd_args); + break; + } + case PTRACE_GETFPREGS: { + struct fpreg *bsd_r = (struct fpreg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + /* Linux is using data where FreeBSD is using addr */ + bsd_args.req = PT_GETFPREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + if (error == 0) + error = map_fpregs_to_linux(bsd_r, &r.fpreg); + if (error == 0) + error = copyout(&r.fpreg, (caddr_t)uap->data, sizeof r.fpreg); + break; + } + case PTRACE_SETFPREGS: { + struct fpreg *bsd_r = (struct fpreg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + /* Linux is using data where FreeBSD is using addr */ + bsd_args.req = PT_SETFPREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = copyin((caddr_t)uap->data, &r.fpreg, sizeof r.fpreg); + if (error == 0) + error = map_fpregs_from_linux(bsd_r, &r.fpreg); + if (error == 0) + error = ptrace(td, &bsd_args); + break; + } + case PTRACE_SETFPXREGS: + case PTRACE_GETFPXREGS: { + struct proc *p; + struct thread *td2; + struct fpreg *bsd_r; + + /* + * Use FreeBSD PT_GETFPREGS for permisson testing. + */ + bsd_r = (struct fpreg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + bsd_args.req = PT_GETFPREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + /* + * If this fails, PTRACE_GETFPXREGS should fail + * for exactly the same reason. + */ + if (error != 0) + break; + + if ((p = pfind(uap->pid)) == NULL) { + error = ESRCH; + break; + } + td2 = FIRST_THREAD_IN_PROC(p); + if (uap->req == PTRACE_GETFPXREGS) { + _PHOLD(p); + error = linux_proc_read_fpxregs(td2, &r.fpxreg); + _PRELE(p); + PROC_UNLOCK(p); + if (error == 0) + error = copyout(&r.fpxreg, + (caddr_t)uap->data, sizeof r.fpxreg); + } else { + error = copyin((caddr_t)uap->data, + &r.fpxreg, sizeof r.fpxreg); + if (error == 0) { + /* clear dangerous bits exactly as Linux does*/ + r.fpxreg.mxcsr &= 0xffbf; + _PHOLD(p); + error = linux_proc_write_fpxregs( + td2, &r.fpxreg); + _PRELE(p); + PROC_UNLOCK(p); + } + } + break; + } + case PTRACE_PEEKUSR: + case PTRACE_POKEUSR: { + error = EIO; + + /* check addr for alignment */ + if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1)) + 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)) { + struct reg *bsd_r; + + bsd_r = (struct reg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + bsd_args.req = PT_GETREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + + error = ptrace(td, &bsd_args); + if (error != 0) + break; + + error = map_regs_to_linux(bsd_r, &r.reg); + if (error != 0) + break; + + if (uap->req == PTRACE_PEEKUSR) { + error = copyout((char *)&r.reg + uap->addr, + (caddr_t)uap->data, sizeof(l_int)); + break; + } + + *(l_int *)((char *)&r.reg + uap->addr) = (l_int)uap->data; + + error = map_regs_from_linux(bsd_r, &r.reg); + if (error != 0) + break; + + bsd_args.req = PT_SETREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + } + + /* + * Simulate debug registers access + */ + if (uap->addr >= LINUX_DBREG_OFFSET && + uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) { + struct dbreg *bsd_r; + + bsd_r = (struct dbreg*)stackgap_alloc(&sg, + sizeof(*bsd_r)); + bsd_args.req = PT_GETDBREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + if (error != 0) + break; + + uap->addr -= LINUX_DBREG_OFFSET; + if (uap->req == PTRACE_PEEKUSR) { + error = copyout((char *)bsd_r + uap->addr, + (caddr_t)uap->data, sizeof(l_int)); + break; + } + + *(l_int *)((char *)bsd_r + uap->addr) = uap->data; + bsd_args.req = PT_SETDBREGS; + bsd_args.addr = (caddr_t)bsd_r; + bsd_args.data = 0; + error = ptrace(td, &bsd_args); + } + + break; + } + case PTRACE_SYSCALL: + /* fall through */ + default: + error = EINVAL; + goto noimpl; + } + return (error); + + noimpl: + printf("linux: ptrace(%u, ...) not implemented\n", + (u_int32_t)uap->req); + return (error); +} Index: i386/linux/linux_dummy.c =================================================================== RCS file: /home/ncvs/src/sys/i386/linux/linux_dummy.c,v retrieving revision 1.32 diff -u -r1.32 linux_dummy.c --- i386/linux/linux_dummy.c 16 Oct 2001 06:15:36 -0000 1.32 +++ i386/linux/linux_dummy.c 24 Dec 2001 22:02:11 -0000 @@ -38,7 +38,6 @@ DUMMY(stat); DUMMY(stime); -DUMMY(ptrace); DUMMY(fstat); DUMMY(olduname); DUMMY(syslog); Index: i386/linux/syscalls.master =================================================================== RCS file: /home/ncvs/src/sys/i386/linux/syscalls.master,v retrieving revision 1.45 diff -u -r1.45 syscalls.master --- i386/linux/syscalls.master 16 Oct 2001 06:11:11 -0000 1.45 +++ i386/linux/syscalls.master 24 Dec 2001 22:02:11 -0000 @@ -65,7 +65,8 @@ 23 STD LINUX { int linux_setuid16(l_uid16_t uid); } 24 STD LINUX { int linux_getuid16(void); } 25 STD LINUX { int linux_stime(void); } -26 STD LINUX { int linux_ptrace(void); } +26 STD LINUX { int linux_ptrace(l_long req, l_long pid, l_long addr, \ + l_long data); } 27 STD LINUX { int linux_alarm(l_uint secs); } 28 STD LINUX { int linux_fstat(l_uint fd, struct ostat *up); } 29 STD LINUX { int linux_pause(void); } Index: modules/linux/Makefile =================================================================== RCS file: /home/ncvs/src/sys/modules/linux/Makefile,v retrieving revision 1.56 diff -u -r1.56 Makefile --- modules/linux/Makefile 22 Feb 2002 18:21:20 -0000 1.56 +++ modules/linux/Makefile 23 Feb 2002 03:16:45 -0000 @@ -8,7 +8,9 @@ SRCS= linux_dummy.c linux_file.c linux_getcwd.c linux_ioctl.c linux_ipc.c \ linux_machdep.c linux_mib.c linux_misc.c linux_signal.c linux_socket.c \ linux_stats.c linux_sysctl.c linux_sysent.c linux_sysvec.c \ - linux_util.c opt_compat.h opt_linux.h opt_vmpage.h vnode_if.h + linux_util.c linux_ptrace.c opt_compat.h opt_linux.h opt_vmpage.h \ + vnode_if.h + OBJS= linux_locore.o .if ${MACHINE_ARCH} == "i386" To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message