Logo Search packages:      
Sourcecode: glibc version File versions

ptrace.c

/* Process tracing interface `ptrace' for GNU Hurd.
   Copyright (C) 1991, 92, 93, 95, 96, 97 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#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>

/* Perform process tracing functions.  REQUEST is one of the values
   in <sys/ptrace.h>, and determines the action to be taken.
   For all requests except PTRACE_TRACEME, PID specifies the process to be
   traced.

   PID and the other arguments described above for the various requests should
   appear (those that are used for the particular request) as:
     pid_t PID, void *ADDR, int DATA, void *ADDR2
   after PID.  */
int
ptrace (enum __ptrace_request request, ... )
{
  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;
}

Generated by  Doxygen 1.6.0   Back to index