Back to index

glibc  2.9
ptrace.c
Go to the documentation of this file.
00001 /* Process tracing interface `ptrace' for GNU Hurd.
00002    Copyright (C) 1991, 92, 93, 95, 96, 97 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <errno.h>
00021 #include <sys/ptrace.h>
00022 #include <sys/types.h>
00023 #include <stdarg.h>
00024 #include <hurd.h>
00025 #include <hurd/signal.h>
00026 #include <hurd/msg.h>
00027 #include <thread_state.h>
00028 
00029 /* Perform process tracing functions.  REQUEST is one of the values
00030    in <sys/ptrace.h>, and determines the action to be taken.
00031    For all requests except PTRACE_TRACEME, PID specifies the process to be
00032    traced.
00033 
00034    PID and the other arguments described above for the various requests should
00035    appear (those that are used for the particular request) as:
00036      pid_t PID, void *ADDR, int DATA, void *ADDR2
00037    after PID.  */
00038 int
00039 ptrace (enum __ptrace_request request, ... )
00040 {
00041   pid_t pid;
00042   void *addr, *addr2;
00043   natural_t data;
00044   va_list ap;
00045 
00046   /* Read data from PID's address space, from ADDR for DATA bytes.  */
00047   error_t read_data (task_t task, vm_address_t *ourpage, vm_size_t *size)
00048     {
00049       /* Read the pages containing the addressed range.  */
00050       error_t err;
00051       *size = round_page (addr + data) - trunc_page (addr);
00052       err = __vm_read (task, trunc_page (addr), *size, ourpage, size);
00053       return err;
00054     }
00055 
00056   /* Fetch the thread port for PID's user thread.  */
00057   error_t fetch_user_thread (task_t task, thread_t *thread)
00058     {
00059       thread_t threadbuf[3], *threads = threadbuf;
00060       mach_msg_type_number_t nthreads = 3, i;
00061       error_t err = __task_threads (task, &threads, &nthreads);
00062       if (err)
00063        return err;
00064       if (nthreads == 0)
00065        return EINVAL;
00066       *thread = threads[0]; /* Assume user thread is first.  */
00067       for (i = 1; i < nthreads; ++i)
00068        __mach_port_deallocate (__mach_task_self (), threads[i]);
00069       if (threads != threadbuf)
00070        __vm_deallocate (__mach_task_self (),
00071                       (vm_address_t) threads, nthreads * sizeof threads[0]);
00072       return 0;
00073     }
00074 
00075   /* Fetch a thread state structure from PID and store it at ADDR.  */
00076   int get_regs (int flavor, mach_msg_type_number_t count)
00077     {
00078       error_t err;
00079       task_t task = __pid2task (pid);
00080       thread_t thread;
00081       if (task == MACH_PORT_NULL)
00082        return -1;
00083       err = fetch_user_thread (task, &thread);
00084       __mach_port_deallocate (__mach_task_self (), task);
00085       if (!err)
00086        err = __thread_get_state (thread, flavor, addr, &count);
00087       __mach_port_deallocate (__mach_task_self (), thread);
00088       return err ? __hurd_fail (err) : 0;
00089     }
00090 
00091 
00092   switch (request)
00093     {
00094     case PTRACE_TRACEME:
00095       /* Make this process be traced.  */
00096       __sigfillset (&_hurdsig_traced);
00097       __USEPORT (PROC, __proc_mark_traced (port));
00098       break;
00099 
00100     case PTRACE_CONT:
00101       va_start (ap, request);
00102       pid = va_arg (ap, pid_t);
00103       addr = va_arg (ap, void *);
00104       data = va_arg (ap, int);
00105       va_end (ap);
00106       {
00107        /* Send a DATA signal to PID, telling it to take the signal
00108           normally even if it's traced.  */
00109        error_t err;
00110        task_t task = __pid2task (pid);
00111        if (task == MACH_PORT_NULL)
00112          return -1;
00113        if (data == SIGKILL)
00114          err = __task_terminate (task);
00115        else
00116          {
00117            if (addr != (void *) 1)
00118              {
00119               /* Move the user thread's PC to ADDR.  */
00120               thread_t thread;
00121               err = fetch_user_thread (task, &thread);
00122               if (!err)
00123                 {
00124                   struct machine_thread_state state;
00125                   mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
00126                   err = __thread_get_state (thread,
00127                                          MACHINE_THREAD_STATE_FLAVOR,
00128                                          (natural_t *) &state, &count);
00129                   if (!err)
00130                     {
00131                      MACHINE_THREAD_STATE_SET_PC (&state, addr);
00132                      err = __thread_set_state (thread,
00133                                             MACHINE_THREAD_STATE_FLAVOR,
00134                                             (natural_t *) &state, count);
00135                     }
00136 
00137                 }
00138               __mach_port_deallocate (__mach_task_self (), thread);
00139              }
00140            else
00141              err = 0;
00142 
00143            if (! err)
00144              /* Tell the process to take the signal (or just resume if 0).  */
00145              err = HURD_MSGPORT_RPC
00146               (__USEPORT (PROC, __proc_getmsgport (port, pid, &msgport)),
00147                0, 0, __msg_sig_post_untraced (msgport, data, 0, task));
00148          }
00149        __mach_port_deallocate (__mach_task_self (), task);
00150        return err ? __hurd_fail (err) : 0;
00151       }
00152 
00153     case PTRACE_KILL:
00154       va_start (ap, request);
00155       pid = va_arg (ap, pid_t);
00156       va_end (ap);
00157       /* SIGKILL always just terminates the task,
00158         so normal kill is just the same when traced.  */
00159       return kill (pid, SIGKILL);
00160 
00161     case PTRACE_SINGLESTEP:
00162       /* This is a machine-dependent kernel RPC on
00163         machines that support it.  Punt.  */
00164       return EOPNOTSUPP;
00165 
00166     case PTRACE_ATTACH:
00167     case PTRACE_DETACH:
00168       va_start (ap, request);
00169       pid = va_arg (ap, pid_t);
00170       va_end (ap);
00171       {
00172        /* Tell PID to set or clear its trace bit.  */
00173        error_t err;
00174        mach_port_t msgport;
00175        task_t task = __pid2task (pid);
00176        if (task == MACH_PORT_NULL)
00177          return -1;
00178        err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
00179        if (! err)
00180          {
00181            err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
00182                                   request == PTRACE_DETACH ? 0 :
00183                                   ~(sigset_t) 0);
00184            if (! err)
00185              {
00186               if (request == PTRACE_ATTACH)
00187                 /* Now stop the process.  */
00188                 err = __msg_sig_post (msgport, SIGSTOP, 0, task);
00189               else
00190                 /* Resume the process from tracing stop.  */
00191                 err = __msg_sig_post_untraced (msgport, 0, 0, task);
00192              }
00193            __mach_port_deallocate (__mach_task_self (), msgport);
00194          }
00195        __mach_port_deallocate (__mach_task_self (), task);
00196        return err ? __hurd_fail (err) : 0;
00197       }
00198 
00199     case PTRACE_PEEKTEXT:
00200     case PTRACE_PEEKDATA:
00201       va_start (ap, request);
00202       pid = va_arg (ap, pid_t);
00203       addr = va_arg (ap, void *);
00204       va_end (ap);
00205       {
00206        /* Read the page (or two pages, if the word lies on a boundary)
00207           containing the addressed word.  */
00208        error_t err;
00209        vm_address_t ourpage;
00210        vm_size_t size;
00211        natural_t word;
00212        task_t task = __pid2task (pid);
00213        if (task == MACH_PORT_NULL)
00214          return -1;
00215        data = sizeof word;
00216        ourpage = 0;
00217        size = 0;
00218        err = read_data (task, &ourpage, &size);
00219        __mach_port_deallocate (__mach_task_self (), task);
00220        if (err)
00221          return __hurd_fail (err);
00222        word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
00223                             + ourpage);
00224        __vm_deallocate (__mach_task_self (), ourpage, size);
00225        return word;
00226       }
00227 
00228     case PTRACE_PEEKUSER:
00229     case PTRACE_POKEUSER:
00230       /* U area, what's that?  */
00231       return EOPNOTSUPP;
00232 
00233     case PTRACE_GETREGS:
00234     case PTRACE_SETREGS:
00235       va_start (ap, request);
00236       pid = va_arg (ap, pid_t);
00237       addr = va_arg (ap, void *);
00238       va_end (ap);
00239       return get_regs (MACHINE_THREAD_STATE_FLAVOR,
00240                      MACHINE_THREAD_STATE_COUNT);
00241 
00242     case PTRACE_GETFPREGS:
00243     case PTRACE_SETFPREGS:
00244       va_start (ap, request);
00245       pid = va_arg (ap, pid_t);
00246       addr = va_arg (ap, void *);
00247       va_end (ap);
00248 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
00249       return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
00250                      MACHINE_THREAD_FLOAT_STATE_COUNT);
00251 #else
00252       return EOPNOTSUPP;
00253 #endif
00254 
00255     case PTRACE_GETFPAREGS:
00256     case PTRACE_SETFPAREGS:
00257       va_start (ap, request);
00258       pid = va_arg (ap, pid_t);
00259       addr = va_arg (ap, void *);
00260       va_end (ap);
00261 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
00262       return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
00263                      MACHINE_THREAD_FPA_STATE_COUNT);
00264 #else
00265       return EOPNOTSUPP;
00266 #endif
00267 
00268     case PTRACE_POKETEXT:
00269     case PTRACE_POKEDATA:
00270       va_start (ap, request);
00271       pid = va_arg (ap, pid_t);
00272       addr = va_arg (ap, void *);
00273       data = va_arg (ap, int);
00274       va_end (ap);
00275       {
00276        /* Read the page (or two pages, if the word lies on a boundary)
00277           containing the addressed word.  */
00278        error_t err;
00279        vm_address_t ourpage;
00280        vm_size_t size;
00281        task_t task = __pid2task (pid);
00282        if (task == MACH_PORT_NULL)
00283          return -1;
00284        data = sizeof (natural_t);
00285        ourpage = 0;
00286        size = 0;
00287        err = read_data (task, &ourpage, &size);
00288 
00289        if (!err)
00290          {
00291            /* Now modify the specified word and write the page back.  */
00292            *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
00293                          + ourpage) = data;
00294            err = __vm_write (task, trunc_page (addr), ourpage, size);
00295            __vm_deallocate (__mach_task_self (), ourpage, size);
00296          }
00297 
00298        __mach_port_deallocate (__mach_task_self (), task);
00299        return err ? __hurd_fail (err) : 0;
00300       }
00301 
00302     case PTRACE_READDATA:
00303     case PTRACE_READTEXT:
00304       va_start (ap, request);
00305       pid = va_arg (ap, pid_t);
00306       addr = va_arg (ap, void *);
00307       data = va_arg (ap, int);
00308       addr2 = va_arg (ap, void *);
00309       va_end (ap);
00310       {
00311        error_t err;
00312        vm_address_t ourpage;
00313        vm_size_t size;
00314        task_t task = __pid2task (pid);
00315        if (task == MACH_PORT_NULL)
00316          return -1;
00317        if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
00318          {
00319            /* Perhaps we can write directly to the user's buffer.  */
00320            ourpage = (vm_address_t) addr2;
00321            size = data;
00322          }
00323        else
00324          {
00325            ourpage = 0;
00326            size = 0;
00327          }
00328        err = read_data (task, &ourpage, &size);
00329        __mach_port_deallocate (__mach_task_self (), task);
00330        if (!err && ourpage != (vm_address_t) addr2)
00331          {
00332            memcpy (addr2, (void *) ourpage, data);
00333            __vm_deallocate (__mach_task_self (), ourpage, size);
00334          }
00335        return err ? __hurd_fail (err) : 0;
00336       }
00337 
00338     case PTRACE_WRITEDATA:
00339     case PTRACE_WRITETEXT:
00340       va_start (ap, request);
00341       pid = va_arg (ap, pid_t);
00342       addr = va_arg (ap, void *);
00343       data = va_arg (ap, int);
00344       addr2 = va_arg (ap, void *);
00345       va_end (ap);
00346       {
00347        error_t err;
00348        vm_address_t ourpage;
00349        vm_size_t size;
00350        task_t task = __pid2task (pid);
00351        if (task == MACH_PORT_NULL)
00352          return -1;
00353        if ((vm_address_t) addr % __vm_page_size == 0 &&
00354            (vm_address_t) data % __vm_page_size == 0)
00355          {
00356            /* Writing whole pages; can go directly from the user's buffer.  */
00357            ourpage = (vm_address_t) addr2;
00358            size = data;
00359            err = 0;
00360          }
00361        else
00362          {
00363            /* Read the task's pages and modify our own copy.  */
00364            ourpage = 0;
00365            size = 0;
00366            err = read_data (task, &ourpage, &size);
00367            if (!err)
00368              memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
00369                             + ourpage),
00370                     addr2,
00371                     data);
00372          }
00373        if (!err)
00374          /* Write back the modified pages.  */
00375          err = __vm_write (task, trunc_page (addr), ourpage, size);
00376        __mach_port_deallocate (__mach_task_self (), task);
00377        return err ? __hurd_fail (err) : 0;
00378       }
00379 
00380     default:
00381       errno = EINVAL;
00382       return -1;
00383     }
00384 
00385   return 0;
00386 }