1 /* $NetBSD: linux_ptrace.c,v 1.17 2012/09/04 00:08:59 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matthias Scheler. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.17 2012/09/04 00:08:59 matt Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/malloc.h> 38 #include <sys/mount.h> 39 #include <sys/proc.h> 40 #include <sys/ptrace.h> 41 #include <sys/systm.h> 42 #include <sys/syscallargs.h> 43 #include <uvm/uvm_extern.h> 44 45 #include <machine/reg.h> 46 #include <machine/pcb.h> 47 48 #include <compat/linux/common/linux_types.h> 49 #include <compat/linux/common/linux_ptrace.h> 50 #include <compat/linux/common/linux_signal.h> 51 52 #include <compat/linux/common/linux_util.h> 53 #include <compat/linux/common/linux_machdep.h> 54 #include <compat/linux/common/linux_emuldata.h> 55 #include <compat/linux/common/linux_exec.h> /* for emul_linux */ 56 57 #include <compat/linux/linux_syscallargs.h> 58 59 #include <lib/libkern/libkern.h> /* for offsetof() */ 60 61 /* 62 * On ARMv2, uregs contains R0--R15, orig_R0. 63 * On ARMv3 and later, it's R0--R15, CPSR, orig_R0. 64 * As far as I can see, Linux doesn't initialise orig_R0 on ARMv2, so we 65 * just produce the ARMv3 version. 66 */ 67 68 struct linux_reg { 69 long uregs[18]; 70 }; 71 72 #define LINUX_REG_R0 0 73 #define LINUX_REG_R1 1 74 #define LINUX_REG_R2 2 75 #define LINUX_REG_R3 3 76 #define LINUX_REG_R4 4 77 #define LINUX_REG_R5 5 78 #define LINUX_REG_R6 6 79 #define LINUX_REG_R7 7 80 #define LINUX_REG_R8 8 81 #define LINUX_REG_R9 9 82 #define LINUX_REG_R10 10 83 #define LINUX_REG_FP 11 84 #define LINUX_REG_IP 12 85 #define LINUX_REG_SP 13 86 #define LINUX_REG_LR 14 87 #define LINUX_REG_PC 15 88 #define LINUX_REG_CPSR 16 89 #define LINUX_REG_ORIG_R0 17 90 91 int linux_ptrace_disabled = 1; /* bitrotted */ 92 93 int 94 linux_sys_ptrace_arch(struct lwp *l, const struct linux_sys_ptrace_args *uap, 95 register_t *retval) 96 { 97 /* { 98 syscallarg(int) request; 99 syscallarg(int) pid; 100 syscallarg(int) addr; 101 syscallarg(int) data; 102 } */ 103 struct proc *p = l->l_proc, *t; 104 struct lwp *lt; 105 #ifdef _ARM_ARCH_6 106 struct pcb *pcb; 107 void *val; 108 #endif 109 struct reg *regs = NULL; 110 struct linux_reg *linux_regs = NULL; 111 int request, error; 112 113 if (linux_ptrace_disabled) 114 return ENOSYS; 115 116 error = 0; 117 request = SCARG(uap, request); 118 regs = kmem_alloc(sizeof(struct reg), KM_SLEEP); 119 linux_regs = kmem_alloc(sizeof(struct linux_reg), KM_SLEEP); 120 121 switch (request) { 122 case LINUX_PTRACE_GETREGS: 123 break; 124 case LINUX_PTRACE_SETREGS: 125 error = copyin((void *)SCARG(uap, data), linux_regs, 126 sizeof(struct linux_reg)); 127 if (error) { 128 goto out; 129 } 130 break; 131 default: 132 error = EIO; 133 goto out; 134 } 135 136 /* Find the process we are supposed to be operating on. */ 137 mutex_enter(proc_lock); 138 if ((t = proc_find(SCARG(uap, pid))) == NULL) { 139 mutex_exit(proc_lock); 140 error = ESRCH; 141 goto out; 142 } 143 mutex_enter(t->p_lock); 144 mutex_exit(proc_lock); 145 146 /* 147 * You cannot do what you want to the process if: 148 * 1. It is not being traced at all, 149 */ 150 if (!ISSET(t->p_slflag, PSL_TRACED)) { 151 mutex_exit(t->p_lock); 152 error = EPERM; 153 goto out; 154 } 155 /* 156 * 2. It is being traced by procfs (which has different signal 157 * delivery semantics), 158 * 3. It is not being traced by _you_, or 159 * 4. It is not currently stopped. 160 */ 161 if (ISSET(t->p_slflag, PSL_FSTRACE) || t->p_pptr != p || 162 t->p_stat != SSTOP || !t->p_waited) { 163 mutex_exit(t->p_lock); 164 error = EBUSY; 165 goto out; 166 } 167 /* XXX: ptrace needs revamp for multi-threading support. */ 168 if (t->p_nlwps > 1) { 169 mutex_exit(t->p_lock); 170 error = ENOSYS; 171 goto out; 172 } 173 lt = LIST_FIRST(&t->p_lwps); 174 *retval = 0; 175 176 switch (request) { 177 case LINUX_PTRACE_GETREGS: 178 error = process_read_regs(lt, regs); 179 mutex_exit(t->p_lock); 180 if (error) { 181 break; 182 } 183 memcpy(linux_regs->uregs, regs->r, 13 * sizeof(register_t)); 184 linux_regs->uregs[LINUX_REG_SP] = regs->r_sp; 185 linux_regs->uregs[LINUX_REG_LR] = regs->r_lr; 186 linux_regs->uregs[LINUX_REG_PC] = regs->r_pc; 187 linux_regs->uregs[LINUX_REG_CPSR] = regs->r_cpsr; 188 linux_regs->uregs[LINUX_REG_ORIG_R0] = regs->r[0]; 189 190 error = copyout(linux_regs, (void *)SCARG(uap, data), 191 sizeof(struct linux_reg)); 192 break; 193 194 case LINUX_PTRACE_SETREGS: 195 memcpy(regs->r, linux_regs->uregs, 13 * sizeof(register_t)); 196 regs->r_sp = linux_regs->uregs[LINUX_REG_SP]; 197 regs->r_lr = linux_regs->uregs[LINUX_REG_LR]; 198 regs->r_pc = linux_regs->uregs[LINUX_REG_PC]; 199 regs->r_cpsr = linux_regs->uregs[LINUX_REG_CPSR]; 200 error = process_write_regs(lt, regs); 201 mutex_exit(t->p_lock); 202 break; 203 204 #ifdef _ARM_ARCH_6 205 #define LINUX_PTRACE_GET_THREAD_AREA 22 206 case LINUX_PTRACE_GET_THREAD_AREA: 207 mutex_exit(t->p_lock); 208 pcb = lwp_getpcb(l); 209 val = (void *)pcb->pcb_un.un_32.pcb32_user_pid_ro; 210 error = copyout(&val, (void *)SCARG(uap, data), sizeof(val)); 211 break; 212 #endif 213 214 default: 215 mutex_exit(t->p_lock); 216 } 217 out: 218 if (regs) 219 kmem_free(regs, sizeof(*regs)); 220 if (linux_regs) 221 kmem_free(linux_regs, sizeof(*linux_regs)); 222 return error; 223 224 } 225