The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/linux/linux_ptrace.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: d812e8f628010780c7020494e1b53e2a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.