Home | History | Annotate | Line # | Download | only in i386
linux_ptrace.c revision 1.5
      1  1.5  jdolecek /*	$NetBSD: linux_ptrace.c,v 1.5 2000/11/21 12:28:15 jdolecek Exp $	*/
      2  1.1      tron 
      3  1.1      tron /*-
      4  1.1      tron  * Copyright (c) 1999 The NetBSD Foundation, Inc.
      5  1.1      tron  * All rights reserved.
      6  1.1      tron  *
      7  1.1      tron  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1      tron  * by Matthias Scheler.
      9  1.1      tron  *
     10  1.1      tron  * Redistribution and use in source and binary forms, with or without
     11  1.1      tron  * modification, are permitted provided that the following conditions
     12  1.1      tron  * are met:
     13  1.1      tron  * 1. Redistributions of source code must retain the above copyright
     14  1.1      tron  *    notice, this list of conditions and the following disclaimer.
     15  1.1      tron  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1      tron  *    notice, this list of conditions and the following disclaimer in the
     17  1.1      tron  *    documentation and/or other materials provided with the distribution.
     18  1.1      tron  * 3. All advertising materials mentioning features or use of this software
     19  1.1      tron  *    must display the following acknowledgement:
     20  1.1      tron  *	This product includes software developed by the NetBSD
     21  1.1      tron  *	Foundation, Inc. and its contributors.
     22  1.1      tron  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  1.1      tron  *    contributors may be used to endorse or promote products derived
     24  1.1      tron  *    from this software without specific prior written permission.
     25  1.1      tron  *
     26  1.1      tron  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  1.1      tron  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  1.1      tron  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  1.1      tron  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  1.1      tron  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  1.1      tron  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  1.1      tron  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  1.1      tron  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  1.1      tron  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  1.1      tron  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  1.1      tron  * POSSIBILITY OF SUCH DAMAGE.
     37  1.1      tron  */
     38  1.1      tron 
     39  1.1      tron #include <sys/types.h>
     40  1.1      tron #include <sys/param.h>
     41  1.5  jdolecek #include <sys/malloc.h>
     42  1.1      tron #include <sys/mount.h>
     43  1.1      tron #include <sys/proc.h>
     44  1.1      tron #include <sys/ptrace.h>
     45  1.1      tron #include <sys/systm.h>
     46  1.1      tron #include <sys/syscallargs.h>
     47  1.5  jdolecek #include <uvm/uvm_extern.h>
     48  1.1      tron 
     49  1.3      tron #include <machine/reg.h>
     50  1.3      tron 
     51  1.1      tron #include <compat/linux/common/linux_types.h>
     52  1.1      tron #include <compat/linux/common/linux_ptrace.h>
     53  1.1      tron #include <compat/linux/common/linux_signal.h>
     54  1.1      tron 
     55  1.1      tron #include <compat/linux/common/linux_util.h>
     56  1.1      tron #include <compat/linux/common/linux_machdep.h>
     57  1.5  jdolecek #include <compat/linux/common/linux_emuldata.h>
     58  1.5  jdolecek #include <compat/linux/common/linux_exec.h>	/* for emul_linux */
     59  1.1      tron 
     60  1.1      tron #include <compat/linux/linux_syscallargs.h>
     61  1.2      tron 
     62  1.5  jdolecek #include <lib/libkern/libkern.h>	/* for offsetof() */
     63  1.5  jdolecek 
     64  1.2      tron struct linux_reg {
     65  1.2      tron 	long ebx;
     66  1.2      tron 	long ecx;
     67  1.2      tron 	long edx;
     68  1.2      tron 	long esi;
     69  1.2      tron 	long edi;
     70  1.2      tron 	long ebp;
     71  1.2      tron 	long eax;
     72  1.5  jdolecek 	long xds, xes;	// unsigned short ds, __ds, es, __es;
     73  1.5  jdolecek 	long __fs, __gs;	// unsigned short fs, __fs, gs, __gs;
     74  1.2      tron 	long orig_eax;
     75  1.2      tron 	long eip;
     76  1.5  jdolecek 	long xcs;	// unsigned short cs, __cs;
     77  1.2      tron 	long eflags;
     78  1.2      tron 	long esp;
     79  1.5  jdolecek 	long xss;	// unsigned short ss, __ss
     80  1.5  jdolecek };
     81  1.5  jdolecek 
     82  1.5  jdolecek /* structure used for storing floating point context */
     83  1.5  jdolecek struct linux_fpreg {
     84  1.5  jdolecek   long cwd;
     85  1.5  jdolecek   long swd;
     86  1.5  jdolecek   long twd;
     87  1.5  jdolecek   long fip;
     88  1.5  jdolecek   long fcs;
     89  1.5  jdolecek   long foo;
     90  1.5  jdolecek   long fos;
     91  1.5  jdolecek   long st_space [20];
     92  1.2      tron };
     93  1.2      tron 
     94  1.5  jdolecek /* user struct for linux process - this is used for Linux ptrace emulation */
     95  1.5  jdolecek /* most of it is junk only used by gdb */
     96  1.5  jdolecek struct linux_user {
     97  1.5  jdolecek   struct linux_reg regs;	/* registers */
     98  1.5  jdolecek   int u_fpvalid;		/* true if math co-processor being used. */
     99  1.5  jdolecek   struct linux_fpreg i387;	/* Math Co-processor registers. */
    100  1.5  jdolecek /* The rest of this junk is to help gdb figure out what goes where */
    101  1.5  jdolecek #define lusr_startgdb	u_tsize
    102  1.5  jdolecek   unsigned long int u_tsize;	/* Text segment size (pages). */
    103  1.5  jdolecek   unsigned long int u_dsize;	/* Data segment size (pages). */
    104  1.5  jdolecek   unsigned long int u_ssize;	/* Stack segment size (pages). */
    105  1.5  jdolecek   unsigned long start_code;     /* Starting virtual address of text. */
    106  1.5  jdolecek   unsigned long start_stack;	/* Starting virtual address of stack area.
    107  1.5  jdolecek 				   This is actually the bottom of the stack,
    108  1.5  jdolecek 				   the top of the stack is always found in the
    109  1.5  jdolecek 				   esp register.  */
    110  1.5  jdolecek   long int __signal;  		/* Signal that caused the core dump. */
    111  1.5  jdolecek   int __reserved;		/* unused */
    112  1.5  jdolecek   void * u_ar0;			/* Used by gdb to help find the values for */
    113  1.5  jdolecek 				/* the registers. */
    114  1.5  jdolecek   struct linux_fpreg* u_fpstate;/* Math Co-processor pointer. */
    115  1.5  jdolecek   unsigned long __magic;	/* To uniquely identify a core file */
    116  1.5  jdolecek   char u_comm[32];		/* User command that was responsible */
    117  1.5  jdolecek   int u_debugreg[8];
    118  1.5  jdolecek #define u_debugreg_end	u_debugreg[7]
    119  1.5  jdolecek };
    120  1.5  jdolecek 
    121  1.5  jdolecek #define LUSR_OFF(member)	offsetof(struct linux_user, member)
    122  1.5  jdolecek #define ISSET(t, f)		((t) & (f))
    123  1.3      tron 
    124  1.1      tron int
    125  1.3      tron linux_sys_ptrace_arch(p, v, retval)
    126  1.1      tron 	struct proc *p;
    127  1.1      tron 	void *v;
    128  1.1      tron 	register_t *retval;
    129  1.1      tron {
    130  1.1      tron 	struct linux_sys_ptrace_args /* {
    131  1.1      tron 		syscallarg(int) request;
    132  1.1      tron 		syscallarg(int) pid;
    133  1.1      tron 		syscallarg(int) addr;
    134  1.1      tron 		syscallarg(int) data;
    135  1.1      tron 	} */ *uap = v;
    136  1.3      tron 	int request, error;
    137  1.3      tron 	struct proc *t;				/* target process */
    138  1.5  jdolecek 	struct reg *regs = NULL;
    139  1.5  jdolecek 	struct fpreg *fpregs = NULL;
    140  1.5  jdolecek 	struct linux_reg *linux_regs = NULL;
    141  1.5  jdolecek 	struct linux_fpreg *linux_fpregs = NULL;
    142  1.5  jdolecek 	int addr;
    143  1.3      tron 
    144  1.3      tron 	request = SCARG(uap, request);
    145  1.3      tron 
    146  1.5  jdolecek 	if ((request != LINUX_PTRACE_PEEKUSR) &&
    147  1.5  jdolecek 	    (request != LINUX_PTRACE_POKEUSR) &&
    148  1.5  jdolecek 	    (request != LINUX_PTRACE_GETREGS) &&
    149  1.3      tron 	    (request != LINUX_PTRACE_SETREGS) &&
    150  1.3      tron 	    (request != LINUX_PTRACE_GETFPREGS) &&
    151  1.3      tron 	    (request != LINUX_PTRACE_SETFPREGS))
    152  1.3      tron 		return EIO;
    153  1.1      tron 
    154  1.3      tron 	/* Find the process we're supposed to be operating on. */
    155  1.3      tron 	if ((t = pfind(SCARG(uap, pid))) == NULL)
    156  1.3      tron 		return ESRCH;
    157  1.3      tron 
    158  1.3      tron 	/*
    159  1.3      tron 	 * You can't do what you want to the process if:
    160  1.3      tron 	 *	(1) It's not being traced at all,
    161  1.3      tron 	 */
    162  1.3      tron 	if (!ISSET(t->p_flag, P_TRACED))
    163  1.3      tron 		return EPERM;
    164  1.3      tron 
    165  1.3      tron 	/*
    166  1.3      tron 	 *	(2) it's being traced by procfs (which has
    167  1.3      tron 	 *	    different signal delivery semantics),
    168  1.3      tron 	 */
    169  1.3      tron 	if (ISSET(t->p_flag, P_FSTRACE))
    170  1.3      tron 		return EBUSY;
    171  1.3      tron 
    172  1.3      tron 	/*
    173  1.3      tron 	 *	(3) it's not being traced by _you_, or
    174  1.3      tron 	 */
    175  1.3      tron 	if (t->p_pptr != p)
    176  1.3      tron 		return EBUSY;
    177  1.3      tron 
    178  1.3      tron 	/*
    179  1.3      tron 	 *	(4) it's not currently stopped.
    180  1.3      tron 	 */
    181  1.3      tron 	if (t->p_stat != SSTOP || !ISSET(t->p_flag, P_WAITED))
    182  1.3      tron 		return EBUSY;
    183  1.3      tron 
    184  1.3      tron 	*retval = 0;
    185  1.3      tron 
    186  1.3      tron 	switch (request) {
    187  1.3      tron 	case  LINUX_PTRACE_GETREGS:
    188  1.5  jdolecek 		MALLOC(regs, struct reg*, sizeof(struct reg), M_TEMP, M_WAITOK);
    189  1.5  jdolecek 		MALLOC(linux_regs, struct linux_reg*, sizeof(struct linux_reg),
    190  1.5  jdolecek 			M_TEMP, M_WAITOK);
    191  1.5  jdolecek 
    192  1.5  jdolecek 		error = process_read_regs(t, regs);
    193  1.3      tron 		if (error != 0)
    194  1.5  jdolecek 			goto out;
    195  1.3      tron 
    196  1.5  jdolecek 		linux_regs->ebx = regs->r_ebx;
    197  1.5  jdolecek 		linux_regs->ecx = regs->r_ecx;
    198  1.5  jdolecek 		linux_regs->edx = regs->r_edx;
    199  1.5  jdolecek 		linux_regs->esi = regs->r_esi;
    200  1.5  jdolecek 		linux_regs->edi = regs->r_edi;
    201  1.5  jdolecek 		linux_regs->ebp = regs->r_ebp;
    202  1.5  jdolecek 		linux_regs->eax = regs->r_eax;
    203  1.5  jdolecek 		linux_regs->xds = regs->r_ds;
    204  1.5  jdolecek 		linux_regs->xes = regs->r_es;
    205  1.5  jdolecek 		linux_regs->orig_eax = regs->r_eax; /* XXX is this correct? */
    206  1.5  jdolecek 		linux_regs->eip = regs->r_cs + regs->r_eip;
    207  1.5  jdolecek 		linux_regs->xcs = regs->r_cs;
    208  1.5  jdolecek 		linux_regs->eflags = regs->r_eflags;
    209  1.5  jdolecek 		linux_regs->esp = regs->r_esp;
    210  1.5  jdolecek 		linux_regs->xss = regs->r_ss;
    211  1.3      tron 
    212  1.5  jdolecek 		error = copyout(linux_regs, (caddr_t)SCARG(uap, data),
    213  1.3      tron 		    sizeof(struct linux_reg));
    214  1.5  jdolecek 		goto out;
    215  1.5  jdolecek 
    216  1.3      tron 	case  LINUX_PTRACE_SETREGS:
    217  1.5  jdolecek 		MALLOC(regs, struct reg*, sizeof(struct reg), M_TEMP, M_WAITOK);
    218  1.5  jdolecek 		MALLOC(linux_regs, struct linux_reg *, sizeof(struct linux_reg),
    219  1.5  jdolecek 			M_TEMP, M_WAITOK);
    220  1.5  jdolecek 
    221  1.5  jdolecek 		error = copyin((caddr_t)SCARG(uap, data), linux_regs,
    222  1.3      tron 		    sizeof(struct linux_reg));
    223  1.3      tron 		if (error != 0)
    224  1.5  jdolecek 			goto out;
    225  1.5  jdolecek 
    226  1.5  jdolecek 		regs->r_ebx = linux_regs->ebx;
    227  1.5  jdolecek 		regs->r_ecx = linux_regs->ecx;
    228  1.5  jdolecek 		regs->r_edx = linux_regs->edx;
    229  1.5  jdolecek 		regs->r_esi = linux_regs->esi;
    230  1.5  jdolecek 		regs->r_edi = linux_regs->edi;
    231  1.5  jdolecek 		regs->r_ebp = linux_regs->ebp;
    232  1.5  jdolecek 		regs->r_eax = linux_regs->eax;
    233  1.5  jdolecek 		regs->r_ds = linux_regs->xds;
    234  1.5  jdolecek 		regs->r_es = linux_regs->xes;
    235  1.5  jdolecek 		regs->r_eip = linux_regs->eip - linux_regs->xcs;
    236  1.5  jdolecek 		regs->r_cs = linux_regs->xcs;
    237  1.5  jdolecek 		regs->r_eflags = linux_regs->eflags;
    238  1.5  jdolecek 		regs->r_esp = linux_regs->esp;
    239  1.5  jdolecek 		regs->r_ss = linux_regs->xss;
    240  1.5  jdolecek 
    241  1.5  jdolecek 		error = process_write_regs(t, regs);
    242  1.5  jdolecek 		goto out;
    243  1.5  jdolecek 
    244  1.5  jdolecek 	case  LINUX_PTRACE_GETFPREGS:
    245  1.5  jdolecek 		MALLOC(fpregs, struct fpreg *, sizeof(struct fpreg),
    246  1.5  jdolecek 			M_TEMP, M_WAITOK);
    247  1.5  jdolecek 		MALLOC(linux_fpregs, struct linux_fpreg *,
    248  1.5  jdolecek 			sizeof(struct linux_fpreg), M_TEMP, M_WAITOK);
    249  1.5  jdolecek 
    250  1.5  jdolecek 		error = process_read_fpregs(t, fpregs);
    251  1.5  jdolecek 		if (error != 0)
    252  1.5  jdolecek 			goto out;
    253  1.3      tron 
    254  1.5  jdolecek 		/* zero the contents if NetBSD fpreg structure is smaller */
    255  1.5  jdolecek 		if (sizeof(struct fpreg) < sizeof(struct linux_fpreg))
    256  1.5  jdolecek 			memset(linux_fpregs, '\0', sizeof(struct linux_fpreg));
    257  1.5  jdolecek 
    258  1.5  jdolecek 		memcpy(linux_fpregs, fpregs,
    259  1.5  jdolecek 			min(sizeof(struct linux_fpreg), sizeof(struct fpreg)));
    260  1.5  jdolecek 		error = copyout(linux_fpregs, (caddr_t)SCARG(uap, data),
    261  1.5  jdolecek 		    sizeof(struct linux_fpreg));
    262  1.5  jdolecek 		goto out;
    263  1.5  jdolecek 
    264  1.5  jdolecek 	case  LINUX_PTRACE_SETFPREGS:
    265  1.5  jdolecek 		MALLOC(fpregs, struct fpreg *, sizeof(struct fpreg),
    266  1.5  jdolecek 			M_TEMP, M_WAITOK);
    267  1.5  jdolecek 		MALLOC(linux_fpregs, struct linux_fpreg *,
    268  1.5  jdolecek 			sizeof(struct linux_fpreg), M_TEMP, M_WAITOK);
    269  1.5  jdolecek 		error = copyin((caddr_t)SCARG(uap, data), linux_fpregs,
    270  1.5  jdolecek 		    sizeof(struct linux_fpreg));
    271  1.5  jdolecek 		if (error != 0)
    272  1.5  jdolecek 			goto out;
    273  1.1      tron 
    274  1.5  jdolecek 		memset(fpregs, '\0', sizeof(struct fpreg));
    275  1.5  jdolecek 		memcpy(fpregs, linux_fpregs,
    276  1.5  jdolecek 			min(sizeof(struct linux_fpreg), sizeof(struct fpreg)));
    277  1.5  jdolecek 
    278  1.5  jdolecek 		error = process_write_regs(t, regs);
    279  1.5  jdolecek 		goto out;
    280  1.5  jdolecek 
    281  1.5  jdolecek 	case  LINUX_PTRACE_PEEKUSR:
    282  1.5  jdolecek 		addr = SCARG(uap, addr);
    283  1.5  jdolecek 
    284  1.5  jdolecek 		PHOLD(t);	/* need full process info */
    285  1.5  jdolecek 		error = 0;
    286  1.5  jdolecek 		if (addr < LUSR_OFF(lusr_startgdb)) {
    287  1.5  jdolecek 			/* XXX should provide appropriate register */
    288  1.5  jdolecek 			error = 1;
    289  1.5  jdolecek 		} else if (addr == LUSR_OFF(u_tsize))
    290  1.5  jdolecek 			*retval = p->p_vmspace->vm_tsize;
    291  1.5  jdolecek 		else if (addr == LUSR_OFF(u_dsize))
    292  1.5  jdolecek 			*retval = p->p_vmspace->vm_dsize;
    293  1.5  jdolecek 		else if (addr == LUSR_OFF(u_ssize))
    294  1.5  jdolecek 			*retval = p->p_vmspace->vm_ssize;
    295  1.5  jdolecek 		else if (addr == LUSR_OFF(start_code))
    296  1.5  jdolecek 			*retval = (register_t) p->p_vmspace->vm_taddr;
    297  1.5  jdolecek 		else if (addr == LUSR_OFF(start_stack))
    298  1.5  jdolecek 			*retval = (register_t) p->p_vmspace->vm_minsaddr;
    299  1.5  jdolecek 		else if (addr == LUSR_OFF(u_ar0))
    300  1.5  jdolecek 			*retval = LUSR_OFF(regs);
    301  1.5  jdolecek 		else if (addr >= LUSR_OFF(u_debugreg)
    302  1.5  jdolecek 			   && addr <= LUSR_OFF(u_debugreg_end)) {
    303  1.5  jdolecek 			int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
    304  1.5  jdolecek 
    305  1.5  jdolecek 			/* only do this for Linux processes */
    306  1.5  jdolecek 			if (t->p_emul != &emul_linux)
    307  1.5  jdolecek 				error = EINVAL;
    308  1.5  jdolecek 			else {
    309  1.5  jdolecek 				*retval = ((struct linux_emuldata *)
    310  1.5  jdolecek 						t->p_emuldata)->debugreg[off];
    311  1.5  jdolecek 			}
    312  1.5  jdolecek 		} else if (addr == LUSR_OFF(__signal)) {
    313  1.5  jdolecek 			error = 1;
    314  1.5  jdolecek 		} else if (addr == LUSR_OFF(__signal)) {
    315  1.5  jdolecek 			error = 1;
    316  1.5  jdolecek 		} else if (addr == LUSR_OFF(u_fpstate)) {
    317  1.5  jdolecek 			error = 1;
    318  1.5  jdolecek 		} else if (addr == LUSR_OFF(__magic)) {
    319  1.5  jdolecek 			error = 1;
    320  1.5  jdolecek 		} else if (addr == LUSR_OFF(u_comm)) {
    321  1.5  jdolecek 			error = 1;
    322  1.5  jdolecek 		} else {
    323  1.5  jdolecek #ifdef DEBUG_LINUX
    324  1.5  jdolecek 			printf("linux_ptrace: unsupported address: %d\n", addr);
    325  1.5  jdolecek #endif
    326  1.5  jdolecek 			error = 1;
    327  1.5  jdolecek 		}
    328  1.5  jdolecek 
    329  1.5  jdolecek 		PRELE(t);
    330  1.5  jdolecek 
    331  1.5  jdolecek 		if (!error)
    332  1.5  jdolecek 			return 0;
    333  1.5  jdolecek 
    334  1.5  jdolecek 	case  LINUX_PTRACE_POKEUSR:
    335  1.5  jdolecek 		/* we only support setting debugregs for now */
    336  1.5  jdolecek 		addr = SCARG(uap, addr);
    337  1.5  jdolecek 		if (addr >= LUSR_OFF(u_debugreg)
    338  1.5  jdolecek 			   && addr <= LUSR_OFF(u_debugreg_end)) {
    339  1.5  jdolecek 			int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
    340  1.5  jdolecek 			int data = SCARG(uap, data);
    341  1.5  jdolecek 
    342  1.5  jdolecek 			/* only do this for Linux processes */
    343  1.5  jdolecek 			if (t->p_emul != &emul_linux)
    344  1.5  jdolecek 				return EINVAL;
    345  1.5  jdolecek 
    346  1.5  jdolecek 			PHOLD(t);
    347  1.5  jdolecek 			((struct linux_emuldata *)t->p_emuldata)->debugreg[off] = data;
    348  1.5  jdolecek 			PRELE(t);
    349  1.5  jdolecek 			return (0);
    350  1.5  jdolecek 		}
    351  1.5  jdolecek 
    352  1.5  jdolecek 		break;
    353  1.5  jdolecek 	default:
    354  1.5  jdolecek 		/* never reached */
    355  1.5  jdolecek 		break;
    356  1.1      tron 	}
    357  1.1      tron 
    358  1.3      tron 	return EIO;
    359  1.5  jdolecek 
    360  1.5  jdolecek     out:
    361  1.5  jdolecek 	if (regs)
    362  1.5  jdolecek 		FREE(regs, M_TEMP);
    363  1.5  jdolecek 	if (fpregs)
    364  1.5  jdolecek 		FREE(fpregs, M_TEMP);
    365  1.5  jdolecek 	if (linux_regs)
    366  1.5  jdolecek 		FREE(linux_regs, M_TEMP);
    367  1.5  jdolecek 	if (linux_fpregs)
    368  1.5  jdolecek 		FREE(linux_fpregs, M_TEMP);
    369  1.5  jdolecek 	return (error);
    370  1.1      tron }
    371