linux_ptrace.c revision 111798
1/* 2 * Copyright (c) 2001 Alexander Kabaev 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD: head/sys/i386/linux/linux_ptrace.c 111798 2003-03-03 09:17:12Z des $ 29 */ 30 31#include "opt_cpu.h" 32 33#include <sys/param.h> 34#include <sys/lock.h> 35#include <sys/mutex.h> 36#include <sys/proc.h> 37#include <sys/ptrace.h> 38#include <sys/syscallsubr.h> 39#include <sys/systm.h> 40 41#include <machine/md_var.h> 42#include <machine/pcb.h> 43#include <machine/reg.h> 44 45#include <i386/linux/linux.h> 46#include <i386/linux/linux_proto.h> 47 48#if !defined(CPU_ENABLE_SSE) && defined(I686_CPU) 49#define CPU_ENABLE_SSE 50#endif 51#if defined(CPU_DISABLE_SSE) 52#undef CPU_ENABLE_SSE 53#endif 54 55/* 56 * Linux ptrace requests numbers. Mostly identical to FreeBSD, 57 * except for MD ones and PT_ATTACH/PT_DETACH. 58 */ 59#define PTRACE_TRACEME 0 60#define PTRACE_PEEKTEXT 1 61#define PTRACE_PEEKDATA 2 62#define PTRACE_PEEKUSR 3 63#define PTRACE_POKETEXT 4 64#define PTRACE_POKEDATA 5 65#define PTRACE_POKEUSR 6 66#define PTRACE_CONT 7 67#define PTRACE_KILL 8 68#define PTRACE_SINGLESTEP 9 69 70#define PTRACE_ATTACH 16 71#define PTRACE_DETACH 17 72 73#define PTRACE_SYSCALL 24 74 75#define PTRACE_GETREGS 12 76#define PTRACE_SETREGS 13 77#define PTRACE_GETFPREGS 14 78#define PTRACE_SETFPREGS 15 79#define PTRACE_GETFPXREGS 18 80#define PTRACE_SETFPXREGS 19 81 82#define PTRACE_SETOPTIONS 21 83 84/* 85 * Linux keeps debug registers at the following 86 * offset in the user struct 87 */ 88#define LINUX_DBREG_OFFSET 252 89#define LINUX_DBREG_SIZE (8*sizeof(l_int)) 90 91static __inline__ int 92map_signum(int signum) 93{ 94 95 if (signum > 0 && signum <= LINUX_SIGTBLSZ) 96 signum = linux_to_bsd_signal[_SIG_IDX(signum)]; 97 return ((signum == SIGSTOP)? 0 : signum); 98} 99 100struct linux_pt_reg { 101 l_long ebx; 102 l_long ecx; 103 l_long edx; 104 l_long esi; 105 l_long edi; 106 l_long ebp; 107 l_long eax; 108 l_int xds; 109 l_int xes; 110 l_int xfs; 111 l_int xgs; 112 l_long orig_eax; 113 l_long eip; 114 l_int xcs; 115 l_long eflags; 116 l_long esp; 117 l_int xss; 118}; 119 120/* 121 * Translate i386 ptrace registers between Linux and FreeBSD formats. 122 * The translation is pretty straighforward, for all registers, but 123 * orig_eax on Linux side and r_trapno and r_err in FreeBSD 124 */ 125static void 126map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) 127{ 128 linux_r->ebx = bsd_r->r_ebx; 129 linux_r->ecx = bsd_r->r_ecx; 130 linux_r->edx = bsd_r->r_edx; 131 linux_r->esi = bsd_r->r_esi; 132 linux_r->edi = bsd_r->r_edi; 133 linux_r->ebp = bsd_r->r_ebp; 134 linux_r->eax = bsd_r->r_eax; 135 linux_r->xds = bsd_r->r_ds; 136 linux_r->xes = bsd_r->r_es; 137 linux_r->xfs = bsd_r->r_fs; 138 linux_r->xgs = bsd_r->r_gs; 139 linux_r->orig_eax = bsd_r->r_eax; 140 linux_r->eip = bsd_r->r_eip; 141 linux_r->xcs = bsd_r->r_cs; 142 linux_r->eflags = bsd_r->r_eflags; 143 linux_r->esp = bsd_r->r_esp; 144 linux_r->xss = bsd_r->r_ss; 145} 146 147static void 148map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r) 149{ 150 bsd_r->r_ebx = linux_r->ebx; 151 bsd_r->r_ecx = linux_r->ecx; 152 bsd_r->r_edx = linux_r->edx; 153 bsd_r->r_esi = linux_r->esi; 154 bsd_r->r_edi = linux_r->edi; 155 bsd_r->r_ebp = linux_r->ebp; 156 bsd_r->r_eax = linux_r->eax; 157 bsd_r->r_ds = linux_r->xds; 158 bsd_r->r_es = linux_r->xes; 159 bsd_r->r_fs = linux_r->xfs; 160 bsd_r->r_gs = linux_r->xgs; 161 bsd_r->r_eip = linux_r->eip; 162 bsd_r->r_cs = linux_r->xcs; 163 bsd_r->r_eflags = linux_r->eflags; 164 bsd_r->r_esp = linux_r->esp; 165 bsd_r->r_ss = linux_r->xss; 166} 167 168struct linux_pt_fpreg { 169 l_long cwd; 170 l_long swd; 171 l_long twd; 172 l_long fip; 173 l_long fcs; 174 l_long foo; 175 l_long fos; 176 l_long st_space[2*10]; 177}; 178 179static void 180map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) 181{ 182 linux_r->cwd = bsd_r->fpr_env[0]; 183 linux_r->swd = bsd_r->fpr_env[1]; 184 linux_r->twd = bsd_r->fpr_env[2]; 185 linux_r->fip = bsd_r->fpr_env[3]; 186 linux_r->fcs = bsd_r->fpr_env[4]; 187 linux_r->foo = bsd_r->fpr_env[5]; 188 linux_r->fos = bsd_r->fpr_env[6]; 189 bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space)); 190} 191 192static void 193map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r) 194{ 195 bsd_r->fpr_env[0] = linux_r->cwd; 196 bsd_r->fpr_env[1] = linux_r->swd; 197 bsd_r->fpr_env[2] = linux_r->twd; 198 bsd_r->fpr_env[3] = linux_r->fip; 199 bsd_r->fpr_env[4] = linux_r->fcs; 200 bsd_r->fpr_env[5] = linux_r->foo; 201 bsd_r->fpr_env[6] = linux_r->fos; 202 bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc)); 203} 204 205struct linux_pt_fpxreg { 206 l_ushort cwd; 207 l_ushort swd; 208 l_ushort twd; 209 l_ushort fop; 210 l_long fip; 211 l_long fcs; 212 l_long foo; 213 l_long fos; 214 l_long mxcsr; 215 l_long reserved; 216 l_long st_space[32]; 217 l_long xmm_space[32]; 218 l_long padding[56]; 219}; 220 221#ifdef CPU_ENABLE_SSE 222static int 223linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) 224{ 225 int error; 226 227 error = 0; 228 mtx_lock_spin(&sched_lock); 229 if (cpu_fxsr == 0 || (td->td_proc->p_sflag & PS_INMEM) == 0) 230 error = EIO; 231 else 232 bcopy(&td->td_pcb->pcb_save.sv_xmm, fpxregs, sizeof(*fpxregs)); 233 mtx_unlock_spin(&sched_lock); 234 return (error); 235} 236 237static int 238linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs) 239{ 240 int error; 241 242 error = 0; 243 mtx_lock_spin(&sched_lock); 244 if (cpu_fxsr == 0 || (td->td_proc->p_sflag & PS_INMEM) == 0) 245 error = EIO; 246 else 247 bcopy(fpxregs, &td->td_pcb->pcb_save.sv_xmm, sizeof(*fpxregs)); 248 mtx_unlock_spin(&sched_lock); 249 return (error); 250} 251#endif 252 253int 254linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) 255{ 256 union { 257 struct linux_pt_reg reg; 258 struct linux_pt_fpreg fpreg; 259 struct linux_pt_fpxreg fpxreg; 260 } r; 261 union { 262 struct reg bsd_reg; 263 struct fpreg bsd_fpreg; 264 struct dbreg bsd_dbreg; 265 } u; 266 void *addr; 267 pid_t pid; 268 int error, req; 269 270 error = 0; 271 272 /* by default, just copy data intact */ 273 req = uap->req; 274 pid = (pid_t)uap->pid; 275 addr = (void *)uap->addr; 276 277 switch (req) { 278 case PTRACE_TRACEME: 279 case PTRACE_POKETEXT: 280 case PTRACE_POKEDATA: 281 case PTRACE_KILL: 282 error = kern_ptrace(td, req, pid, addr, uap->data); 283 break; 284 case PTRACE_PEEKTEXT: 285 case PTRACE_PEEKDATA: { 286 /* need to preserve return value */ 287 int rval = td->td_retval[0]; 288 error = kern_ptrace(td, req, pid, addr, 0); 289 if (error == 0) 290 error = copyout(td->td_retval, (void *)uap->data, 291 sizeof(l_int)); 292 td->td_retval[0] = rval; 293 break; 294 } 295 case PTRACE_DETACH: 296 error = kern_ptrace(td, PT_DETACH, pid, (void *)1, 297 map_signum(uap->data)); 298 break; 299 case PTRACE_SINGLESTEP: 300 case PTRACE_CONT: 301 error = kern_ptrace(td, req, pid, (void *)1, 302 map_signum(uap->data)); 303 break; 304 case PTRACE_ATTACH: 305 error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); 306 break; 307 case PTRACE_GETREGS: 308 /* Linux is using data where FreeBSD is using addr */ 309 error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0); 310 if (error == 0) { 311 map_regs_to_linux(&u.bsd_reg, &r.reg); 312 error = copyout(&r.reg, (void *)uap->data, 313 sizeof(r.reg)); 314 } 315 break; 316 case PTRACE_SETREGS: 317 /* Linux is using data where FreeBSD is using addr */ 318 error = copyin((void *)uap->data, &r.reg, sizeof(r.reg)); 319 if (error == 0) { 320 map_regs_from_linux(&u.bsd_reg, &r.reg); 321 error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0); 322 } 323 break; 324 case PTRACE_GETFPREGS: 325 /* Linux is using data where FreeBSD is using addr */ 326 error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0); 327 if (error == 0) { 328 map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg); 329 error = copyout(&r.fpreg, (void *)uap->data, 330 sizeof(r.fpreg)); 331 } 332 break; 333 case PTRACE_SETFPREGS: 334 /* Linux is using data where FreeBSD is using addr */ 335 error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg)); 336 if (error == 0) { 337 map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg); 338 error = kern_ptrace(td, PT_SETFPREGS, pid, 339 &u.bsd_fpreg, 0); 340 } 341 break; 342 case PTRACE_SETFPXREGS: 343#ifdef CPU_ENABLE_SSE 344 error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg)); 345 if (error) 346 break; 347#endif 348 /* FALL THROUGH */ 349 case PTRACE_GETFPXREGS: { 350#ifdef CPU_ENABLE_SSE 351 struct proc *p; 352 struct thread *td2; 353 354 if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) { 355 static int once = 0; 356 if (!once) { 357 printf("linux: savexmm != linux_pt_fpxreg\n"); 358 once = 1; 359 } 360 error = EIO; 361 break; 362 } 363 364 if ((p = pfind(uap->pid)) == NULL) { 365 error = ESRCH; 366 break; 367 } 368 369 if ((error = p_candebug(td, p)) != 0) 370 goto fail; 371 372 /* System processes can't be debugged. */ 373 if ((p->p_flag & P_SYSTEM) != 0) { 374 error = EINVAL; 375 goto fail; 376 } 377 378 /* not being traced... */ 379 if ((p->p_flag & P_TRACED) == 0) { 380 error = EPERM; 381 goto fail; 382 } 383 384 /* not being traced by YOU */ 385 if (p->p_pptr != td->td_proc) { 386 error = EBUSY; 387 goto fail; 388 } 389 390 /* not currently stopped */ 391 if ((p->p_flag & (P_TRACED|P_WAITED)) == 0) { 392 error = EBUSY; 393 goto fail; 394 } 395 396 td2 = FIRST_THREAD_IN_PROC(p); 397 if (req == PTRACE_GETFPXREGS) { 398 _PHOLD(p); 399 error = linux_proc_read_fpxregs(td2, &r.fpxreg); 400 _PRELE(p); 401 PROC_UNLOCK(p); 402 if (error == 0) 403 error = copyout(&r.fpxreg, (void *)uap->data, 404 sizeof(r.fpxreg)); 405 } else { 406 /* clear dangerous bits exactly as Linux does*/ 407 r.fpxreg.mxcsr &= 0xffbf; 408 _PHOLD(p); 409 error = linux_proc_write_fpxregs(td2, &r.fpxreg); 410 _PRELE(p); 411 PROC_UNLOCK(p); 412 } 413 break; 414 415 fail: 416 PROC_UNLOCK(p); 417#else 418 error = EIO; 419#endif 420 break; 421 } 422 case PTRACE_PEEKUSR: 423 case PTRACE_POKEUSR: { 424 error = EIO; 425 426 /* check addr for alignment */ 427 if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1)) 428 break; 429 /* 430 * Allow linux programs to access register values in 431 * user struct. We simulate this through PT_GET/SETREGS 432 * as necessary. 433 */ 434 if (uap->addr < sizeof(struct linux_pt_reg)) { 435 error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0); 436 if (error != 0) 437 break; 438 439 map_regs_to_linux(&u.bsd_reg, &r.reg); 440 if (req == PTRACE_PEEKUSR) { 441 error = copyout((char *)&r.reg + uap->addr, 442 (void *)uap->data, sizeof(l_int)); 443 break; 444 } 445 446 *(l_int *)((char *)&r.reg + uap->addr) = 447 (l_int)uap->data; 448 449 map_regs_from_linux(&u.bsd_reg, &r.reg); 450 error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0); 451 } 452 453 /* 454 * Simulate debug registers access 455 */ 456 if (uap->addr >= LINUX_DBREG_OFFSET && 457 uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) { 458 error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg, 459 0); 460 if (error != 0) 461 break; 462 463 uap->addr -= LINUX_DBREG_OFFSET; 464 if (req == PTRACE_PEEKUSR) { 465 error = copyout((char *)&u.bsd_dbreg + 466 uap->addr, (void *)uap->data, 467 sizeof(l_int)); 468 break; 469 } 470 471 *(l_int *)((char *)&u.bsd_dbreg + uap->addr) = 472 uap->data; 473 error = kern_ptrace(td, PT_SETDBREGS, pid, 474 &u.bsd_dbreg, 0); 475 } 476 477 break; 478 } 479 case PTRACE_SYSCALL: 480 /* fall through */ 481 default: 482 printf("linux: ptrace(%u, ...) not implemented\n", 483 (unsigned int)uap->req); 484 error = EINVAL; 485 break; 486 } 487 488 return (error); 489} 490