Home | History | Annotate | Line # | Download | only in i386
      1  1.36   tsutsui /*	$NetBSD: linux_ptrace.c,v 1.36 2022/09/05 14:14:42 tsutsui 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  *
     19   1.1      tron  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.1      tron  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.1      tron  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.1      tron  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.1      tron  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.1      tron  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.1      tron  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.1      tron  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.1      tron  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.1      tron  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.1      tron  * POSSIBILITY OF SUCH DAMAGE.
     30   1.1      tron  */
     31   1.7     lukem 
     32   1.7     lukem #include <sys/cdefs.h>
     33  1.36   tsutsui __KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.36 2022/09/05 14:14:42 tsutsui Exp $");
     34   1.1      tron 
     35   1.1      tron #include <sys/param.h>
     36   1.1      tron #include <sys/mount.h>
     37   1.1      tron #include <sys/proc.h>
     38   1.1      tron #include <sys/ptrace.h>
     39   1.1      tron #include <sys/systm.h>
     40  1.15      matt #include <sys/syscall.h>
     41   1.1      tron #include <sys/syscallargs.h>
     42   1.5  jdolecek #include <uvm/uvm_extern.h>
     43   1.1      tron 
     44   1.3      tron #include <machine/reg.h>
     45   1.3      tron 
     46   1.1      tron #include <compat/linux/common/linux_types.h>
     47   1.1      tron #include <compat/linux/common/linux_ptrace.h>
     48   1.1      tron #include <compat/linux/common/linux_signal.h>
     49   1.1      tron 
     50   1.1      tron #include <compat/linux/common/linux_util.h>
     51   1.1      tron #include <compat/linux/common/linux_machdep.h>
     52   1.5  jdolecek #include <compat/linux/common/linux_emuldata.h>
     53   1.5  jdolecek #include <compat/linux/common/linux_exec.h>	/* for emul_linux */
     54   1.1      tron 
     55   1.1      tron #include <compat/linux/linux_syscallargs.h>
     56   1.2      tron 
     57   1.5  jdolecek #include <lib/libkern/libkern.h>	/* for offsetof() */
     58   1.5  jdolecek 
     59   1.2      tron struct linux_reg {
     60   1.2      tron 	long ebx;
     61   1.2      tron 	long ecx;
     62   1.2      tron 	long edx;
     63   1.2      tron 	long esi;
     64   1.2      tron 	long edi;
     65   1.2      tron 	long ebp;
     66   1.2      tron 	long eax;
     67   1.6     lukem 	long xds, xes;		/* unsigned short ds, __ds, es, __es; */
     68   1.6     lukem 	long __fs, __gs;	/* unsigned short fs, __fs, gs, __gs; */
     69   1.2      tron 	long orig_eax;
     70   1.2      tron 	long eip;
     71   1.6     lukem 	long xcs;		/* unsigned short cs, __cs; */
     72   1.2      tron 	long eflags;
     73   1.2      tron 	long esp;
     74   1.6     lukem 	long xss;		/* unsigned short ss, __ss; */
     75   1.5  jdolecek };
     76   1.5  jdolecek 
     77   1.5  jdolecek /* structure used for storing floating point context */
     78  1.10  junyoung struct linux_fpctx {
     79  1.10  junyoung 	long cwd;
     80  1.10  junyoung 	long swd;
     81  1.10  junyoung 	long twd;
     82  1.10  junyoung 	long fip;
     83  1.10  junyoung 	long fcs;
     84  1.10  junyoung 	long foo;
     85  1.10  junyoung 	long fos;
     86  1.10  junyoung 	long st_space[20];
     87   1.2      tron };
     88   1.2      tron 
     89   1.5  jdolecek /* user struct for linux process - this is used for Linux ptrace emulation */
     90   1.5  jdolecek /* most of it is junk only used by gdb */
     91   1.5  jdolecek struct linux_user {
     92  1.10  junyoung 	struct linux_reg regs;		/* registers */
     93  1.10  junyoung 	int u_fpvalid;		/* true if math co-processor being used. */
     94  1.10  junyoung 	struct linux_fpctx i387;	/* Math Co-processor registers. */
     95   1.5  jdolecek /* The rest of this junk is to help gdb figure out what goes where */
     96   1.5  jdolecek #define lusr_startgdb	u_tsize
     97  1.10  junyoung 	unsigned long int u_tsize;	/* Text segment size (pages). */
     98  1.10  junyoung 	unsigned long int u_dsize;	/* Data segment size (pages). */
     99  1.10  junyoung 	unsigned long int u_ssize;	/* Stack segment size (pages). */
    100  1.10  junyoung 	unsigned long start_code;	/* Starting virtual address of text. */
    101  1.12     perry 	unsigned long start_stack;	/* Starting virtual address of stack
    102  1.10  junyoung 					   area. This is actually the bottom of
    103  1.10  junyoung 					   the stack, the top of the stack is
    104  1.10  junyoung 					   always found in the esp register. */
    105  1.10  junyoung 	long int __signal;  		/* Signal that caused the core dump. */
    106  1.10  junyoung 	int __reserved;			/* unused */
    107  1.10  junyoung 	void *u_ar0;			/* Used by gdb to help find the values
    108  1.10  junyoung 					   for the registers. */
    109  1.10  junyoung 	struct linux_fpctx *u_fpstate;	/* Math Co-processor pointer. */
    110  1.10  junyoung 	unsigned long __magic;		/* To uniquely identify a core file */
    111  1.10  junyoung 	char u_comm[32];		/* User command that was responsible */
    112  1.10  junyoung 	int u_debugreg[8];
    113   1.5  jdolecek #define u_debugreg_end	u_debugreg[7]
    114   1.5  jdolecek };
    115   1.5  jdolecek 
    116   1.5  jdolecek #define LUSR_OFF(member)	offsetof(struct linux_user, member)
    117   1.3      tron 
    118  1.21        ad int linux_ptrace_disabled = 1;	/* bitrotted */
    119  1.21        ad 
    120   1.1      tron int
    121  1.25     rmind linux_sys_ptrace_arch(struct lwp *l, const struct linux_sys_ptrace_args *uap,
    122  1.25     rmind     register_t *retval)
    123   1.1      tron {
    124  1.20       dsl 	/* {
    125   1.1      tron 		syscallarg(int) request;
    126   1.1      tron 		syscallarg(int) pid;
    127   1.1      tron 		syscallarg(int) addr;
    128   1.1      tron 		syscallarg(int) data;
    129  1.20       dsl 	} */
    130  1.25     rmind 	struct proc *p = l->l_proc, *t;
    131  1.11   thorpej 	struct lwp *lt;
    132   1.5  jdolecek 	struct reg *regs = NULL;
    133   1.5  jdolecek 	struct fpreg *fpregs = NULL;
    134   1.5  jdolecek 	struct linux_reg *linux_regs = NULL;
    135  1.10  junyoung 	struct linux_fpctx *linux_fpregs = NULL;
    136  1.26       chs 	struct linux_emuldata *led;
    137  1.25     rmind 	int request, error, addr;
    138  1.27       dsl 	size_t fp_size;
    139   1.3      tron 
    140  1.21        ad 	if (linux_ptrace_disabled)
    141  1.21        ad 		return ENOSYS;
    142  1.21        ad 
    143  1.25     rmind 	error = 0;
    144   1.3      tron 	request = SCARG(uap, request);
    145   1.3      tron 
    146  1.25     rmind 	switch (request) {
    147  1.25     rmind 	case LINUX_PTRACE_PEEKUSR:
    148  1.25     rmind 	case LINUX_PTRACE_POKEUSR:
    149  1.25     rmind 		break;
    150  1.25     rmind 	case LINUX_PTRACE_GETREGS:
    151  1.25     rmind 	case LINUX_PTRACE_SETREGS:
    152  1.25     rmind 		regs = kmem_alloc(sizeof(struct reg), KM_SLEEP);
    153  1.25     rmind 		linux_regs = kmem_alloc(sizeof(struct linux_reg), KM_SLEEP);
    154  1.25     rmind 		if (request == LINUX_PTRACE_SETREGS) {
    155  1.25     rmind 			error = copyin((void *)SCARG(uap, data), linux_regs,
    156  1.25     rmind 			    sizeof(struct linux_reg));
    157  1.25     rmind 			if (error) {
    158  1.25     rmind 				goto out;
    159  1.25     rmind 			}
    160  1.25     rmind 		}
    161  1.25     rmind 		break;
    162  1.25     rmind 	case LINUX_PTRACE_GETFPREGS:
    163  1.25     rmind 	case LINUX_PTRACE_SETFPREGS:
    164  1.25     rmind 		fpregs = kmem_alloc(sizeof(struct fpreg), KM_SLEEP);
    165  1.25     rmind 		linux_fpregs = kmem_alloc(sizeof(struct linux_fpctx), KM_SLEEP);
    166  1.25     rmind 		if (request == LINUX_PTRACE_SETFPREGS) {
    167  1.25     rmind 			error = copyin((void *)SCARG(uap, data), linux_fpregs,
    168  1.25     rmind 			    sizeof(struct linux_fpctx));
    169  1.25     rmind 			if (error) {
    170  1.25     rmind 				goto out;
    171  1.25     rmind 			}
    172  1.25     rmind 		}
    173  1.25     rmind 		break;
    174  1.25     rmind 	default:
    175  1.25     rmind 		error = EIO;
    176  1.25     rmind 		goto out;
    177  1.25     rmind 	}
    178   1.1      tron 
    179  1.25     rmind 	/* Find the process we are supposed to be operating on. */
    180  1.34        ad 	mutex_enter(&proc_lock);
    181  1.25     rmind 	if ((t = proc_find(SCARG(uap, pid))) == NULL) {
    182  1.34        ad 		mutex_exit(&proc_lock);
    183  1.29  christos 		error = ESRCH;
    184  1.29  christos 		goto out;
    185  1.25     rmind 	}
    186  1.25     rmind 	mutex_enter(t->p_lock);
    187   1.3      tron 
    188   1.3      tron 	/*
    189  1.25     rmind 	 * You cannot do what you want to the process if:
    190  1.25     rmind 	 * 1. It is not being traced at all,
    191   1.3      tron 	 */
    192  1.25     rmind 	if (!ISSET(t->p_slflag, PSL_TRACED)) {
    193  1.25     rmind 		mutex_exit(t->p_lock);
    194  1.34        ad 		mutex_exit(&proc_lock);
    195  1.25     rmind 		error = EPERM;
    196  1.25     rmind 		goto out;
    197  1.25     rmind 	}
    198   1.3      tron 	/*
    199  1.32     kamil 	 * 2. It is not being traced by _you_, or
    200  1.32     kamil 	 * 3. It is not currently stopped.
    201   1.3      tron 	 */
    202  1.32     kamil 	if (t->p_pptr != p || t->p_stat != SSTOP || !t->p_waited) {
    203  1.25     rmind 		mutex_exit(t->p_lock);
    204  1.34        ad 		mutex_exit(&proc_lock);
    205  1.25     rmind 		error = EBUSY;
    206  1.25     rmind 		goto out;
    207  1.25     rmind 	}
    208  1.34        ad 	mutex_exit(&proc_lock);
    209  1.25     rmind 	/* XXX: ptrace needs revamp for multi-threading support. */
    210  1.25     rmind 	if (t->p_nlwps > 1) {
    211  1.25     rmind 		mutex_exit(t->p_lock);
    212  1.25     rmind 		error = ENOSYS;
    213  1.25     rmind 		goto out;
    214  1.25     rmind 	}
    215  1.11   thorpej 	lt = LIST_FIRST(&t->p_lwps);
    216   1.3      tron 	*retval = 0;
    217   1.3      tron 
    218   1.3      tron 	switch (request) {
    219  1.25     rmind 	case LINUX_PTRACE_GETREGS:
    220  1.11   thorpej 		error = process_read_regs(lt, regs);
    221  1.25     rmind 		mutex_exit(t->p_lock);
    222  1.25     rmind 		if (error) {
    223  1.25     rmind 			break;
    224  1.25     rmind 		}
    225  1.35  riastrad 		memset(linux_regs, 0, sizeof(*linux_regs));
    226   1.5  jdolecek 		linux_regs->ebx = regs->r_ebx;
    227   1.5  jdolecek 		linux_regs->ecx = regs->r_ecx;
    228   1.5  jdolecek 		linux_regs->edx = regs->r_edx;
    229   1.5  jdolecek 		linux_regs->esi = regs->r_esi;
    230   1.5  jdolecek 		linux_regs->edi = regs->r_edi;
    231   1.5  jdolecek 		linux_regs->ebp = regs->r_ebp;
    232   1.5  jdolecek 		linux_regs->eax = regs->r_eax;
    233   1.5  jdolecek 		linux_regs->xds = regs->r_ds;
    234   1.5  jdolecek 		linux_regs->xes = regs->r_es;
    235   1.5  jdolecek 		linux_regs->orig_eax = regs->r_eax; /* XXX is this correct? */
    236   1.5  jdolecek 		linux_regs->eip = regs->r_cs + regs->r_eip;
    237   1.5  jdolecek 		linux_regs->xcs = regs->r_cs;
    238   1.5  jdolecek 		linux_regs->eflags = regs->r_eflags;
    239   1.5  jdolecek 		linux_regs->esp = regs->r_esp;
    240   1.5  jdolecek 		linux_regs->xss = regs->r_ss;
    241   1.3      tron 
    242  1.17  christos 		error = copyout(linux_regs, (void *)SCARG(uap, data),
    243   1.3      tron 		    sizeof(struct linux_reg));
    244  1.25     rmind 		break;
    245   1.5  jdolecek 
    246  1.25     rmind 	case LINUX_PTRACE_SETREGS:
    247   1.5  jdolecek 		regs->r_ebx = linux_regs->ebx;
    248   1.5  jdolecek 		regs->r_ecx = linux_regs->ecx;
    249   1.5  jdolecek 		regs->r_edx = linux_regs->edx;
    250   1.5  jdolecek 		regs->r_esi = linux_regs->esi;
    251   1.5  jdolecek 		regs->r_edi = linux_regs->edi;
    252   1.5  jdolecek 		regs->r_ebp = linux_regs->ebp;
    253   1.5  jdolecek 		regs->r_eax = linux_regs->eax;
    254   1.5  jdolecek 		regs->r_ds = linux_regs->xds;
    255   1.5  jdolecek 		regs->r_es = linux_regs->xes;
    256   1.5  jdolecek 		regs->r_eip = linux_regs->eip - linux_regs->xcs;
    257   1.5  jdolecek 		regs->r_cs = linux_regs->xcs;
    258   1.5  jdolecek 		regs->r_eflags = linux_regs->eflags;
    259   1.5  jdolecek 		regs->r_esp = linux_regs->esp;
    260   1.5  jdolecek 		regs->r_ss = linux_regs->xss;
    261   1.5  jdolecek 
    262  1.11   thorpej 		error = process_write_regs(lt, regs);
    263  1.25     rmind 		mutex_exit(t->p_lock);
    264  1.25     rmind 		break;
    265   1.5  jdolecek 
    266  1.25     rmind 	case LINUX_PTRACE_GETFPREGS:
    267  1.27       dsl 		fp_size = sizeof fpregs;
    268  1.27       dsl 		error = process_read_fpregs(lt, fpregs, &fp_size);
    269  1.25     rmind 		mutex_exit(t->p_lock);
    270  1.25     rmind 		if (error) {
    271  1.25     rmind 			break;
    272  1.25     rmind 		}
    273  1.25     rmind 		/* Zero the contents if NetBSD fpreg structure is smaller */
    274  1.27       dsl 		if (fp_size < sizeof(struct linux_fpctx)) {
    275  1.10  junyoung 			memset(linux_fpregs, '\0', sizeof(struct linux_fpctx));
    276  1.25     rmind 		}
    277   1.5  jdolecek 		memcpy(linux_fpregs, fpregs,
    278  1.33  riastrad 		    uimin(sizeof(struct linux_fpctx), fp_size));
    279  1.17  christos 		error = copyout(linux_fpregs, (void *)SCARG(uap, data),
    280  1.10  junyoung 		    sizeof(struct linux_fpctx));
    281  1.25     rmind 		break;
    282   1.1      tron 
    283  1.25     rmind 	case LINUX_PTRACE_SETFPREGS:
    284   1.5  jdolecek 		memset(fpregs, '\0', sizeof(struct fpreg));
    285   1.5  jdolecek 		memcpy(fpregs, linux_fpregs,
    286  1.33  riastrad 		    uimin(sizeof(struct linux_fpctx), sizeof(struct fpreg)));
    287  1.11   thorpej 		error = process_write_regs(lt, regs);
    288  1.25     rmind 		mutex_exit(t->p_lock);
    289  1.25     rmind 		break;
    290   1.5  jdolecek 
    291  1.25     rmind 	case LINUX_PTRACE_PEEKUSR:
    292  1.25     rmind 		/* XXX locking */
    293   1.5  jdolecek 		addr = SCARG(uap, addr);
    294   1.5  jdolecek 
    295   1.5  jdolecek 		error = 0;
    296   1.5  jdolecek 		if (addr < LUSR_OFF(lusr_startgdb)) {
    297   1.5  jdolecek 			/* XXX should provide appropriate register */
    298  1.26       chs 			error = ENOTSUP;
    299   1.5  jdolecek 		} else if (addr == LUSR_OFF(u_tsize))
    300   1.5  jdolecek 			*retval = p->p_vmspace->vm_tsize;
    301   1.5  jdolecek 		else if (addr == LUSR_OFF(u_dsize))
    302   1.5  jdolecek 			*retval = p->p_vmspace->vm_dsize;
    303   1.5  jdolecek 		else if (addr == LUSR_OFF(u_ssize))
    304   1.5  jdolecek 			*retval = p->p_vmspace->vm_ssize;
    305   1.5  jdolecek 		else if (addr == LUSR_OFF(start_code))
    306   1.5  jdolecek 			*retval = (register_t) p->p_vmspace->vm_taddr;
    307   1.5  jdolecek 		else if (addr == LUSR_OFF(start_stack))
    308   1.5  jdolecek 			*retval = (register_t) p->p_vmspace->vm_minsaddr;
    309   1.5  jdolecek 		else if (addr == LUSR_OFF(u_ar0))
    310   1.5  jdolecek 			*retval = LUSR_OFF(regs);
    311   1.5  jdolecek 		else if (addr >= LUSR_OFF(u_debugreg)
    312   1.5  jdolecek 			   && addr <= LUSR_OFF(u_debugreg_end)) {
    313   1.5  jdolecek 			int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
    314   1.5  jdolecek 
    315   1.5  jdolecek 			/* only do this for Linux processes */
    316  1.26       chs 			if (t->p_emul != &emul_linux) {
    317  1.26       chs 				mutex_exit(t->p_lock);
    318  1.26       chs 				return EINVAL;
    319   1.5  jdolecek 			}
    320  1.26       chs 
    321  1.26       chs 			led = lt->l_emuldata;
    322  1.26       chs 			*retval = led->led_debugreg[off];
    323   1.5  jdolecek 		} else if (addr == LUSR_OFF(__signal)) {
    324  1.26       chs 			error = ENOTSUP;
    325   1.5  jdolecek 		} else if (addr == LUSR_OFF(u_fpstate)) {
    326  1.26       chs 			error = ENOTSUP;
    327   1.5  jdolecek 		} else if (addr == LUSR_OFF(__magic)) {
    328  1.26       chs 			error = ENOTSUP;
    329   1.5  jdolecek 		} else if (addr == LUSR_OFF(u_comm)) {
    330  1.26       chs 			error = ENOTSUP;
    331   1.5  jdolecek 		} else {
    332   1.5  jdolecek #ifdef DEBUG_LINUX
    333   1.5  jdolecek 			printf("linux_ptrace: unsupported address: %d\n", addr);
    334   1.5  jdolecek #endif
    335  1.26       chs 			error = ENOTSUP;
    336   1.5  jdolecek 		}
    337  1.26       chs 		mutex_exit(t->p_lock);
    338  1.26       chs 		break;
    339   1.5  jdolecek 
    340  1.25     rmind 	case LINUX_PTRACE_POKEUSR:
    341   1.5  jdolecek 		/* we only support setting debugregs for now */
    342   1.5  jdolecek 		addr = SCARG(uap, addr);
    343  1.26       chs 		if (addr >= LUSR_OFF(u_debugreg) &&
    344  1.26       chs 		    addr <= LUSR_OFF(u_debugreg_end)) {
    345   1.5  jdolecek 			int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
    346   1.5  jdolecek 			int data = SCARG(uap, data);
    347   1.5  jdolecek 
    348   1.5  jdolecek 			/* only do this for Linux processes */
    349  1.25     rmind 			if (t->p_emul != &emul_linux) {
    350  1.26       chs 				mutex_exit(t->p_lock);
    351  1.26       chs 				return EINVAL;
    352  1.25     rmind 			}
    353  1.26       chs 			led = lt->l_emuldata;
    354  1.26       chs 			led->led_debugreg[off] = data;
    355   1.5  jdolecek 		}
    356  1.26       chs 		mutex_exit(t->p_lock);
    357  1.26       chs 		break;
    358  1.26       chs 
    359   1.5  jdolecek 	default:
    360  1.25     rmind 		mutex_exit(t->p_lock);
    361   1.5  jdolecek 		break;
    362   1.1      tron 	}
    363  1.25     rmind out:
    364   1.5  jdolecek 	if (regs)
    365  1.25     rmind 		kmem_free(regs, sizeof(*regs));
    366  1.25     rmind 	if (linux_regs)
    367  1.25     rmind 		kmem_free(linux_regs, sizeof(*linux_regs));
    368   1.5  jdolecek 	if (fpregs)
    369  1.25     rmind 		kmem_free(fpregs, sizeof(*fpregs));
    370   1.5  jdolecek 	if (linux_fpregs)
    371  1.25     rmind 		kmem_free(linux_fpregs, sizeof(*linux_fpregs));
    372  1.25     rmind 
    373  1.25     rmind 	return error;
    374   1.1      tron }
    375