Back to index

glibc  2.9
Functions
ptrace.c File Reference
#include <errno.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <stdarg.h>
#include <hurd.h>
#include <hurd/signal.h>
#include <hurd/msg.h>
#include <thread_state.h>

Go to the source code of this file.

Functions

int ptrace (enum __ptrace_request request,...)

Function Documentation

int ptrace ( enum __ptrace_request  request,
  ... 
)

Definition at line 39 of file ptrace.c.

{
  pid_t pid;
  void *addr, *addr2;
  natural_t data;
  va_list ap;

  /* Read data from PID's address space, from ADDR for DATA bytes.  */
  error_t read_data (task_t task, vm_address_t *ourpage, vm_size_t *size)
    {
      /* Read the pages containing the addressed range.  */
      error_t err;
      *size = round_page (addr + data) - trunc_page (addr);
      err = __vm_read (task, trunc_page (addr), *size, ourpage, size);
      return err;
    }

  /* Fetch the thread port for PID's user thread.  */
  error_t fetch_user_thread (task_t task, thread_t *thread)
    {
      thread_t threadbuf[3], *threads = threadbuf;
      mach_msg_type_number_t nthreads = 3, i;
      error_t err = __task_threads (task, &threads, &nthreads);
      if (err)
       return err;
      if (nthreads == 0)
       return EINVAL;
      *thread = threads[0]; /* Assume user thread is first.  */
      for (i = 1; i < nthreads; ++i)
       __mach_port_deallocate (__mach_task_self (), threads[i]);
      if (threads != threadbuf)
       __vm_deallocate (__mach_task_self (),
                      (vm_address_t) threads, nthreads * sizeof threads[0]);
      return 0;
    }

  /* Fetch a thread state structure from PID and store it at ADDR.  */
  int get_regs (int flavor, mach_msg_type_number_t count)
    {
      error_t err;
      task_t task = __pid2task (pid);
      thread_t thread;
      if (task == MACH_PORT_NULL)
       return -1;
      err = fetch_user_thread (task, &thread);
      __mach_port_deallocate (__mach_task_self (), task);
      if (!err)
       err = __thread_get_state (thread, flavor, addr, &count);
      __mach_port_deallocate (__mach_task_self (), thread);
      return err ? __hurd_fail (err) : 0;
    }


  switch (request)
    {
    case PTRACE_TRACEME:
      /* Make this process be traced.  */
      __sigfillset (&_hurdsig_traced);
      __USEPORT (PROC, __proc_mark_traced (port));
      break;

    case PTRACE_CONT:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      data = va_arg (ap, int);
      va_end (ap);
      {
       /* Send a DATA signal to PID, telling it to take the signal
          normally even if it's traced.  */
       error_t err;
       task_t task = __pid2task (pid);
       if (task == MACH_PORT_NULL)
         return -1;
       if (data == SIGKILL)
         err = __task_terminate (task);
       else
         {
           if (addr != (void *) 1)
             {
              /* Move the user thread's PC to ADDR.  */
              thread_t thread;
              err = fetch_user_thread (task, &thread);
              if (!err)
                {
                  struct machine_thread_state state;
                  mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
                  err = __thread_get_state (thread,
                                         MACHINE_THREAD_STATE_FLAVOR,
                                         (natural_t *) &state, &count);
                  if (!err)
                    {
                     MACHINE_THREAD_STATE_SET_PC (&state, addr);
                     err = __thread_set_state (thread,
                                            MACHINE_THREAD_STATE_FLAVOR,
                                            (natural_t *) &state, count);
                    }

                }
              __mach_port_deallocate (__mach_task_self (), thread);
             }
           else
             err = 0;

           if (! err)
             /* Tell the process to take the signal (or just resume if 0).  */
             err = HURD_MSGPORT_RPC
              (__USEPORT (PROC, __proc_getmsgport (port, pid, &msgport)),
               0, 0, __msg_sig_post_untraced (msgport, data, 0, task));
         }
       __mach_port_deallocate (__mach_task_self (), task);
       return err ? __hurd_fail (err) : 0;
      }

    case PTRACE_KILL:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      va_end (ap);
      /* SIGKILL always just terminates the task,
        so normal kill is just the same when traced.  */
      return kill (pid, SIGKILL);

    case PTRACE_SINGLESTEP:
      /* This is a machine-dependent kernel RPC on
        machines that support it.  Punt.  */
      return EOPNOTSUPP;

    case PTRACE_ATTACH:
    case PTRACE_DETACH:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      va_end (ap);
      {
       /* Tell PID to set or clear its trace bit.  */
       error_t err;
       mach_port_t msgport;
       task_t task = __pid2task (pid);
       if (task == MACH_PORT_NULL)
         return -1;
       err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
       if (! err)
         {
           err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
                                  request == PTRACE_DETACH ? 0 :
                                  ~(sigset_t) 0);
           if (! err)
             {
              if (request == PTRACE_ATTACH)
                /* Now stop the process.  */
                err = __msg_sig_post (msgport, SIGSTOP, 0, task);
              else
                /* Resume the process from tracing stop.  */
                err = __msg_sig_post_untraced (msgport, 0, 0, task);
             }
           __mach_port_deallocate (__mach_task_self (), msgport);
         }
       __mach_port_deallocate (__mach_task_self (), task);
       return err ? __hurd_fail (err) : 0;
      }

    case PTRACE_PEEKTEXT:
    case PTRACE_PEEKDATA:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      va_end (ap);
      {
       /* Read the page (or two pages, if the word lies on a boundary)
          containing the addressed word.  */
       error_t err;
       vm_address_t ourpage;
       vm_size_t size;
       natural_t word;
       task_t task = __pid2task (pid);
       if (task == MACH_PORT_NULL)
         return -1;
       data = sizeof word;
       ourpage = 0;
       size = 0;
       err = read_data (task, &ourpage, &size);
       __mach_port_deallocate (__mach_task_self (), task);
       if (err)
         return __hurd_fail (err);
       word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
                            + ourpage);
       __vm_deallocate (__mach_task_self (), ourpage, size);
       return word;
      }

    case PTRACE_PEEKUSER:
    case PTRACE_POKEUSER:
      /* U area, what's that?  */
      return EOPNOTSUPP;

    case PTRACE_GETREGS:
    case PTRACE_SETREGS:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      va_end (ap);
      return get_regs (MACHINE_THREAD_STATE_FLAVOR,
                     MACHINE_THREAD_STATE_COUNT);

    case PTRACE_GETFPREGS:
    case PTRACE_SETFPREGS:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      va_end (ap);
#ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
      return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
                     MACHINE_THREAD_FLOAT_STATE_COUNT);
#else
      return EOPNOTSUPP;
#endif

    case PTRACE_GETFPAREGS:
    case PTRACE_SETFPAREGS:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      va_end (ap);
#ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
      return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
                     MACHINE_THREAD_FPA_STATE_COUNT);
#else
      return EOPNOTSUPP;
#endif

    case PTRACE_POKETEXT:
    case PTRACE_POKEDATA:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      data = va_arg (ap, int);
      va_end (ap);
      {
       /* Read the page (or two pages, if the word lies on a boundary)
          containing the addressed word.  */
       error_t err;
       vm_address_t ourpage;
       vm_size_t size;
       task_t task = __pid2task (pid);
       if (task == MACH_PORT_NULL)
         return -1;
       data = sizeof (natural_t);
       ourpage = 0;
       size = 0;
       err = read_data (task, &ourpage, &size);

       if (!err)
         {
           /* Now modify the specified word and write the page back.  */
           *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
                         + ourpage) = data;
           err = __vm_write (task, trunc_page (addr), ourpage, size);
           __vm_deallocate (__mach_task_self (), ourpage, size);
         }

       __mach_port_deallocate (__mach_task_self (), task);
       return err ? __hurd_fail (err) : 0;
      }

    case PTRACE_READDATA:
    case PTRACE_READTEXT:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      data = va_arg (ap, int);
      addr2 = va_arg (ap, void *);
      va_end (ap);
      {
       error_t err;
       vm_address_t ourpage;
       vm_size_t size;
       task_t task = __pid2task (pid);
       if (task == MACH_PORT_NULL)
         return -1;
       if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
         {
           /* Perhaps we can write directly to the user's buffer.  */
           ourpage = (vm_address_t) addr2;
           size = data;
         }
       else
         {
           ourpage = 0;
           size = 0;
         }
       err = read_data (task, &ourpage, &size);
       __mach_port_deallocate (__mach_task_self (), task);
       if (!err && ourpage != (vm_address_t) addr2)
         {
           memcpy (addr2, (void *) ourpage, data);
           __vm_deallocate (__mach_task_self (), ourpage, size);
         }
       return err ? __hurd_fail (err) : 0;
      }

    case PTRACE_WRITEDATA:
    case PTRACE_WRITETEXT:
      va_start (ap, request);
      pid = va_arg (ap, pid_t);
      addr = va_arg (ap, void *);
      data = va_arg (ap, int);
      addr2 = va_arg (ap, void *);
      va_end (ap);
      {
       error_t err;
       vm_address_t ourpage;
       vm_size_t size;
       task_t task = __pid2task (pid);
       if (task == MACH_PORT_NULL)
         return -1;
       if ((vm_address_t) addr % __vm_page_size == 0 &&
           (vm_address_t) data % __vm_page_size == 0)
         {
           /* Writing whole pages; can go directly from the user's buffer.  */
           ourpage = (vm_address_t) addr2;
           size = data;
           err = 0;
         }
       else
         {
           /* Read the task's pages and modify our own copy.  */
           ourpage = 0;
           size = 0;
           err = read_data (task, &ourpage, &size);
           if (!err)
             memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
                            + ourpage),
                    addr2,
                    data);
         }
       if (!err)
         /* Write back the modified pages.  */
         err = __vm_write (task, trunc_page (addr), ourpage, size);
       __mach_port_deallocate (__mach_task_self (), task);
       return err ? __hurd_fail (err) : 0;
      }

    default:
      errno = EINVAL;
      return -1;
    }

  return 0;
}