1 /* $NetBSD: linux_ptrace.c,v 1.21 2008/12/17 20:51:33 cegger Exp $ */ 2 3 /*- 4 * Copyright (c) 1999, 2001 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 and Emmanuel Dreyfus. 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 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.21 2008/12/17 20:51:33 cegger Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/malloc.h> 37 #include <sys/mount.h> 38 #include <sys/proc.h> 39 #include <sys/ptrace.h> 40 #include <sys/systm.h> 41 #include <sys/syscallargs.h> 42 #include <uvm/uvm_extern.h> 43 44 #include <machine/reg.h> 45 46 #include <compat/linux/common/linux_types.h> 47 #include <compat/linux/common/linux_ptrace.h> 48 #include <compat/linux/common/linux_signal.h> 49 50 #include <compat/linux/common/linux_util.h> 51 #include <compat/linux/common/linux_machdep.h> 52 #include <compat/linux/common/linux_emuldata.h> 53 #include <compat/linux/common/linux_exec.h> /* for emul_linux */ 54 55 #include <compat/linux/linux_syscallargs.h> 56 57 #include <lib/libkern/libkern.h> /* for offsetof() */ 58 59 /* 60 * From Linux's include/asm-ppc/ptrace.h. 61 * structure used for storing reg context: defined in linux_machdep.h 62 * structure used for storing floating point context: 63 * Linux just uses a double fpr[32], no struct 64 */ 65 66 /* 67 * user struct for linux process - this is used for Linux ptrace emulation 68 * most of it is junk only used by gdb 69 * 70 * From Linux's include/asm-ppc/user.h 71 * 72 * XXX u_ar0 was a struct reg in Linux/powerpc 73 * Can't find out what a struct regs is for Linux/powerpc, 74 * so we use a struct pt_regs instead. don't know if this is right. 75 */ 76 77 struct linux_user { 78 struct linux_pt_regs regs; 79 #define lusr_startgdb regs 80 size_t u_tsize; 81 size_t u_dsize; 82 size_t u_ssize; 83 unsigned long start_code; 84 unsigned long start_data; 85 unsigned long start_stack; 86 long int signal; 87 struct linux_pt_regs *u_ar0; /* help gdb find registers */ 88 unsigned long magic; 89 char u_comm[32]; 90 #define lu_comm_end u_comm[31] 91 }; 92 93 #define LUSR_OFF(member) offsetof(struct linux_user, member) 94 #define LUSR_REG_OFF(member) offsetof(struct linux_pt_regs, member) 95 #define ISSET(t, f) ((t) & (f)) 96 97 int linux_ptrace_disabled = 1; /* bitrotted */ 98 99 /* XXX Check me! (From NetBSD/i386) */ 100 int 101 linux_sys_ptrace_arch(struct lwp *l, const struct linux_sys_ptrace_args *uap, register_t *retval) 102 { 103 /* { 104 syscallarg(int) request; 105 syscallarg(int) pid; 106 syscallarg(int) addr; 107 syscallarg(int) data; 108 } */ 109 int request, error; 110 struct proc *p = l->l_proc; 111 struct proc *t; /* target process */ 112 struct lwp *lt; 113 struct reg *regs = NULL; 114 struct fpreg *fpregs = NULL; 115 struct linux_pt_regs *linux_regs = NULL; 116 double *linux_fpreg = NULL; /* it's an array, not a struct */ 117 int addr; 118 int i; 119 120 if (linux_ptrace_disabled) 121 return ENOSYS; 122 123 switch (request = SCARG(uap, request)) { 124 case LINUX_PTRACE_PEEKUSR: 125 case LINUX_PTRACE_POKEUSR: 126 case LINUX_PTRACE_GETREGS: 127 case LINUX_PTRACE_SETREGS: 128 case LINUX_PTRACE_GETFPREGS: 129 case LINUX_PTRACE_SETFPREGS: 130 break; 131 default: 132 return EIO; 133 } 134 135 /* Find the process we're supposed to be operating on. */ 136 if ((t = pfind(SCARG(uap, pid))) == NULL) 137 return ESRCH; 138 139 /* 140 * You can't do what you want to the process if: 141 * (1) It's not being traced at all, 142 */ 143 if (!ISSET(t->p_slflag, PSL_TRACED)) /* XXXSMP */ 144 return EPERM; 145 146 /* 147 * (2) it's being traced by procfs (which has 148 * different signal delivery semantics), 149 */ 150 if (ISSET(t->p_slflag, PSL_FSTRACE)) 151 return EBUSY; 152 153 /* 154 * (3) it's not being traced by _you_, or 155 */ 156 if (t->p_pptr != p) 157 return EBUSY; 158 159 /* 160 * (4) it's not currently stopped. 161 */ 162 if (t->p_stat != SSTOP || !t->p_waited) 163 return EBUSY; 164 165 lt = LIST_FIRST(&t->p_lwps); 166 *retval = 0; 167 168 switch (request) { 169 case LINUX_PTRACE_GETREGS: 170 regs = malloc(sizeof(struct reg), M_TEMP, M_WAITOK); 171 linux_regs = malloc(sizeof(*linux_regs), M_TEMP, M_WAITOK); 172 173 error = process_read_regs(lt, regs); 174 if (error != 0) 175 goto out; 176 177 for (i=0; i<=31; i++) 178 linux_regs->lgpr[i] = regs->fixreg[i]; 179 linux_regs->lnip = regs->pc; 180 linux_regs->lmsr = 0; 181 /* XXX Is that right? */ 182 linux_regs->lorig_gpr3 = regs->fixreg[3]; 183 linux_regs->lctr = regs->ctr; 184 linux_regs->llink = regs->lr; 185 linux_regs->lxer = regs->xer; 186 linux_regs->lccr = regs->cr; 187 linux_regs->lmq = 0; 188 linux_regs->ltrap = 0; 189 linux_regs->ldar = 0; 190 linux_regs->ldsisr = 0; 191 linux_regs->lresult = 0; 192 193 error = copyout(linux_regs, (void *)SCARG(uap, data), 194 sizeof(struct linux_pt_regs)); 195 goto out; 196 197 case LINUX_PTRACE_SETREGS: 198 regs = malloc(sizeof(struct reg), M_TEMP, M_WAITOK); 199 linux_regs = malloc(sizeof(*linux_regs), M_TEMP, M_WAITOK); 200 201 error = copyin((void *)SCARG(uap, data), linux_regs, 202 sizeof(struct linux_pt_regs)); 203 if (error != 0) 204 goto out; 205 206 for (i=0; i<=31; i++) 207 regs->fixreg[i] = linux_regs->lgpr[i]; 208 regs->lr = linux_regs->llink; 209 regs->cr = linux_regs->lccr; 210 regs->xer = linux_regs->lxer; 211 regs->ctr = linux_regs->lctr; 212 regs->pc = linux_regs->lnip; /* XXX */ 213 214 error = process_write_regs(lt, regs); 215 goto out; 216 217 case LINUX_PTRACE_GETFPREGS: 218 fpregs = malloc(sizeof(struct fpreg), M_TEMP, M_WAITOK); 219 linux_fpreg = malloc(32*sizeof(double), M_TEMP, M_WAITOK); 220 221 error = process_read_fpregs(lt, fpregs); 222 if (error != 0) 223 goto out; 224 225 /* zero the contents if NetBSD fpreg structure is smaller */ 226 if (sizeof(struct fpreg) < (32*sizeof(double))) 227 memset(linux_fpreg, '\0', (32*sizeof(double))); 228 229 memcpy(linux_fpreg, fpregs, 230 min(32*sizeof(double), sizeof(struct fpreg))); 231 error = copyout(linux_fpreg, (void *)SCARG(uap, data), 232 32*sizeof(double)); 233 goto out; 234 235 case LINUX_PTRACE_SETFPREGS: 236 fpregs = malloc(sizeof(struct fpreg), M_TEMP, M_WAITOK); 237 linux_fpreg = malloc(32*sizeof(double), M_TEMP, M_WAITOK); 238 error = copyin((void *)SCARG(uap, data), linux_fpreg, 239 32*sizeof(double)); 240 if (error != 0) 241 goto out; 242 243 memset(fpregs, '\0', sizeof(struct fpreg)); 244 memcpy(fpregs, linux_fpreg, 245 min(32*sizeof(double), sizeof(struct fpreg))); 246 247 error = process_write_fpregs(lt, fpregs); 248 goto out; 249 250 case LINUX_PTRACE_PEEKUSR: 251 addr = SCARG(uap, addr); 252 regs = malloc(sizeof(struct reg), M_TEMP, M_WAITOK); 253 error = process_read_regs(lt, regs); 254 if (error) 255 goto out; 256 257 uvm_lwp_hold(lt); /* need full process info */ 258 error = 0; 259 if ((addr < LUSR_OFF(lusr_startgdb)) || 260 (addr > LUSR_OFF(lu_comm_end))) 261 error = 1; 262 else if (addr == LUSR_OFF(u_tsize)) 263 *retval = p->p_vmspace->vm_tsize; 264 else if (addr == LUSR_OFF(u_dsize)) 265 *retval = p->p_vmspace->vm_dsize; 266 else if (addr == LUSR_OFF(u_ssize)) 267 *retval = p->p_vmspace->vm_ssize; 268 else if (addr == LUSR_OFF(start_code)) 269 *retval = (register_t) p->p_vmspace->vm_taddr; 270 else if (addr == LUSR_OFF(start_stack)) 271 *retval = (register_t) p->p_vmspace->vm_minsaddr; 272 else if ((addr >= LUSR_REG_OFF(lpt_regs_fixreg_begin)) && 273 (addr <= LUSR_REG_OFF(lpt_regs_fixreg_end))) 274 *retval = regs->fixreg[addr / sizeof (register_t)]; 275 else if (addr == LUSR_REG_OFF(lnip)) 276 *retval = regs->pc; 277 else if (addr == LUSR_REG_OFF(lctr)) 278 *retval = regs->ctr; 279 else if (addr == LUSR_REG_OFF(llink)) 280 *retval = regs->lr; 281 else if (addr == LUSR_REG_OFF(lxer)) 282 *retval = regs->xer; 283 else if (addr == LUSR_REG_OFF(lccr)) 284 *retval = regs->cr; 285 else if (addr == LUSR_OFF(signal)) 286 error = 1; 287 else if (addr == LUSR_OFF(signal)) 288 error = 1; 289 else if (addr == LUSR_OFF(magic)) 290 error = 1; 291 else if (addr == LUSR_OFF(u_comm)) 292 error = 1; 293 else { 294 #ifdef DEBUG_LINUX 295 printf("linux_ptrace: unsupported address: %d\n", addr); 296 #endif 297 error = 1; 298 } 299 300 uvm_lwp_rele(lt); 301 302 if (error) 303 goto out; 304 305 error = copyout (retval, 306 (void *)SCARG(uap, data), sizeof retval); 307 *retval = SCARG(uap, data); 308 309 goto out; 310 311 break; 312 313 case LINUX_PTRACE_POKEUSR: /* XXX Not tested */ 314 addr = SCARG(uap, addr); 315 regs = malloc(sizeof(struct reg), M_TEMP, M_WAITOK); 316 error = process_read_regs(lt, regs); 317 if (error) 318 goto out; 319 320 uvm_lwp_hold(lt); /* need full process info */ 321 error = 0; 322 if ((addr < LUSR_OFF(lusr_startgdb)) || 323 (addr > LUSR_OFF(lu_comm_end))) 324 error = 1; 325 else if (addr == LUSR_OFF(u_tsize)) 326 error = 1; 327 else if (addr == LUSR_OFF(u_dsize)) 328 error = 1; 329 else if (addr == LUSR_OFF(u_ssize)) 330 error = 1; 331 else if (addr == LUSR_OFF(start_code)) 332 error = 1; 333 else if (addr == LUSR_OFF(start_stack)) 334 error = 1; 335 else if ((addr >= LUSR_REG_OFF(lpt_regs_fixreg_begin)) && 336 (addr <= LUSR_REG_OFF(lpt_regs_fixreg_end))) 337 regs->fixreg[addr / sizeof (register_t)] = 338 (register_t)SCARG(uap, data); 339 else if (addr == LUSR_REG_OFF(lnip)) 340 regs->pc = (register_t)SCARG(uap, data); 341 else if (addr == LUSR_REG_OFF(lctr)) 342 regs->ctr = (register_t)SCARG(uap, data); 343 else if (addr == LUSR_REG_OFF(llink)) 344 regs->lr = (register_t)SCARG(uap, data); 345 else if (addr == LUSR_REG_OFF(lxer)) 346 regs->xer = (register_t)SCARG(uap, data); 347 else if (addr == LUSR_REG_OFF(lccr)) 348 regs->cr = (register_t)SCARG(uap, data); 349 else if (addr == LUSR_OFF(signal)) 350 error = 1; 351 else if (addr == LUSR_OFF(magic)) 352 error = 1; 353 else if (addr == LUSR_OFF(u_comm)) 354 error = 1; 355 else { 356 #ifdef DEBUG_LINUX 357 printf("linux_ptrace: unsupported address: %d\n", addr); 358 #endif 359 error = 1; 360 } 361 362 uvm_lwp_rele(lt); 363 364 error = process_write_regs(lt,regs); 365 if (error) 366 goto out; 367 368 break; 369 default: 370 /* never reached */ 371 break; 372 } 373 return EIO; 374 375 out: 376 if (regs) 377 free(regs, M_TEMP); 378 if (fpregs) 379 free(fpregs, M_TEMP); 380 if (linux_regs) 381 free(linux_regs, M_TEMP); 382 if (linux_fpreg) 383 free(linux_fpreg, M_TEMP); 384 return (error); 385 } 386