Logo Search packages:      
Sourcecode: linux version File versions  Download package

ptrace.c

/* ptrace.c: FRV specific parts of process tracing
 *
 * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 * - Derived from arch/m68k/kernel/ptrace.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>

#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/processor.h>
#include <asm/unistd.h>

/*
 * does not yet catch signals sent when the child dies.
 * in exit.c or in signal.c.
 */

/*
 * Get contents of register REGNO in task TASK.
 */
static inline long get_reg(struct task_struct *task, int regno)
{
      struct user_context *user = task->thread.user;

      if (regno < 0 || regno >= PT__END)
            return 0;

      return ((unsigned long *) user)[regno];
}

/*
 * Write contents of register REGNO in task TASK.
 */
static inline int put_reg(struct task_struct *task, int regno,
                    unsigned long data)
{
      struct user_context *user = task->thread.user;

      if (regno < 0 || regno >= PT__END)
            return -EIO;

      switch (regno) {
      case PT_GR(0):
            return 0;
      case PT_PSR:
      case PT__STATUS:
            return -EIO;
      default:
            ((unsigned long *) user)[regno] = data;
            return 0;
      }
}

/*
 * check that an address falls within the bounds of the target process's memory mappings
 */
static inline int is_user_addr_valid(struct task_struct *child,
                             unsigned long start, unsigned long len)
{
#ifdef CONFIG_MMU
      if (start >= PAGE_OFFSET || len > PAGE_OFFSET - start)
            return -EIO;
      return 0;
#else
      struct vm_list_struct *vml;

      for (vml = child->mm->context.vmlist; vml; vml = vml->next)
            if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end)
                  return 0;

      return -EIO;
#endif
}

/*
 * Called by kernel/ptrace.c when detaching..
 *
 * Control h/w single stepping
 */
void ptrace_disable(struct task_struct *child)
{
      child->thread.frame0->__status &= ~REG__STATUS_STEP;
}

void ptrace_enable(struct task_struct *child)
{
      child->thread.frame0->__status |= REG__STATUS_STEP;
}

long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
      unsigned long tmp;
      int ret;

      switch (request) {
            /* when I and D space are separate, these will need to be fixed. */
      case PTRACE_PEEKTEXT: /* read word at location addr. */
      case PTRACE_PEEKDATA:
            ret = -EIO;
            if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
                  break;
            ret = generic_ptrace_peekdata(child, addr, data);
            break;

            /* read the word at location addr in the USER area. */
      case PTRACE_PEEKUSR: {
            tmp = 0;
            ret = -EIO;
            if ((addr & 3) || addr < 0)
                  break;

            ret = 0;
            switch (addr >> 2) {
            case 0 ... PT__END - 1:
                  tmp = get_reg(child, addr >> 2);
                  break;

            case PT__END + 0:
                  tmp = child->mm->end_code - child->mm->start_code;
                  break;

            case PT__END + 1:
                  tmp = child->mm->end_data - child->mm->start_data;
                  break;

            case PT__END + 2:
                  tmp = child->mm->start_stack - child->mm->start_brk;
                  break;

            case PT__END + 3:
                  tmp = child->mm->start_code;
                  break;

            case PT__END + 4:
                  tmp = child->mm->start_stack;
                  break;

            default:
                  ret = -EIO;
                  break;
            }

            if (ret == 0)
                  ret = put_user(tmp, (unsigned long *) data);
            break;
      }

            /* when I and D space are separate, this will have to be fixed. */
      case PTRACE_POKETEXT: /* write the word at location addr. */
      case PTRACE_POKEDATA:
            ret = -EIO;
            if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
                  break;
            ret = generic_ptrace_pokedata(child, addr, data);
            break;

      case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
            ret = -EIO;
            if ((addr & 3) || addr < 0)
                  break;

            ret = 0;
            switch (addr >> 2) {
            case 0 ... PT__END-1:
                  ret = put_reg(child, addr >> 2, data);
                  break;

            default:
                  ret = -EIO;
                  break;
            }
            break;

      case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
      case PTRACE_CONT: /* restart after signal. */
            ret = -EIO;
            if (!valid_signal(data))
                  break;
            if (request == PTRACE_SYSCALL)
                  set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
            else
                  clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
            child->exit_code = data;
            ptrace_disable(child);
            wake_up_process(child);
            ret = 0;
            break;

            /* make the child exit.  Best I can do is send it a sigkill.
             * perhaps it should be put in the status that it wants to
             * exit.
             */
      case PTRACE_KILL:
            ret = 0;
            if (child->exit_state == EXIT_ZOMBIE)     /* already dead */
                  break;
            child->exit_code = SIGKILL;
            clear_tsk_thread_flag(child, TIF_SINGLESTEP);
            ptrace_disable(child);
            wake_up_process(child);
            break;

      case PTRACE_SINGLESTEP:  /* set the trap flag. */
            ret = -EIO;
            if (!valid_signal(data))
                  break;
            clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
            ptrace_enable(child);
            child->exit_code = data;
            wake_up_process(child);
            ret = 0;
            break;

      case PTRACE_DETACH:     /* detach a process that was attached. */
            ret = ptrace_detach(child, data);
            break;

      case PTRACE_GETREGS: { /* Get all integer regs from the child. */
            int i;
            for (i = 0; i < PT__GPEND; i++) {
                  tmp = get_reg(child, i);
                  if (put_user(tmp, (unsigned long *) data)) {
                        ret = -EFAULT;
                        break;
                  }
                  data += sizeof(long);
            }
            ret = 0;
            break;
      }

      case PTRACE_SETREGS: { /* Set all integer regs in the child. */
            int i;
            for (i = 0; i < PT__GPEND; i++) {
                  if (get_user(tmp, (unsigned long *) data)) {
                        ret = -EFAULT;
                        break;
                  }
                  put_reg(child, i, tmp);
                  data += sizeof(long);
            }
            ret = 0;
            break;
      }

      case PTRACE_GETFPREGS: { /* Get the child FP/Media state. */
            ret = 0;
            if (copy_to_user((void *) data,
                         &child->thread.user->f,
                         sizeof(child->thread.user->f)))
                  ret = -EFAULT;
            break;
      }

      case PTRACE_SETFPREGS: { /* Set the child FP/Media state. */
            ret = 0;
            if (copy_from_user(&child->thread.user->f,
                           (void *) data,
                           sizeof(child->thread.user->f)))
                  ret = -EFAULT;
            break;
      }

      case PTRACE_GETFDPIC:
            tmp = 0;
            switch (addr) {
            case PTRACE_GETFDPIC_EXEC:
                  tmp = child->mm->context.exec_fdpic_loadmap;
                  break;
            case PTRACE_GETFDPIC_INTERP:
                  tmp = child->mm->context.interp_fdpic_loadmap;
                  break;
            default:
                  break;
            }

            ret = 0;
            if (put_user(tmp, (unsigned long *) data)) {
                  ret = -EFAULT;
                  break;
            }
            break;

      default:
            ret = -EIO;
            break;
      }
      return ret;
}

int __nongprelbss kstrace;

static const struct {
      const char  *name;
      unsigned    argmask;
} __syscall_name_table[NR_syscalls] = {
      [0]   = { "restart_syscall"               },
      [1]   = { "exit",       0x000001    },
      [2]   = { "fork",       0xffffff    },
      [3]   = { "read",       0x000141    },
      [4]   = { "write",            0x000141    },
      [5]   = { "open",       0x000235    },
      [6]   = { "close",            0x000001    },
      [7]   = { "waitpid",          0x000141    },
      [8]   = { "creat",            0x000025    },
      [9]   = { "link",       0x000055    },
      [10]  = { "unlink",           0x000005    },
      [11]  = { "execve",           0x000445    },
      [12]  = { "chdir",            0x000005    },
      [13]  = { "time",       0x000004    },
      [14]  = { "mknod",            0x000325    },
      [15]  = { "chmod",            0x000025    },
      [16]  = { "lchown",           0x000025    },
      [17]  = { "break" },
      [18]  = { "oldstat",          0x000045    },
      [19]  = { "lseek",            0x000131    },
      [20]  = { "getpid",           0xffffff    },
      [21]  = { "mount",            0x043555    },
      [22]  = { "umount",           0x000005    },
      [23]  = { "setuid",           0x000001    },
      [24]  = { "getuid",           0xffffff    },
      [25]  = { "stime",            0x000004    },
      [26]  = { "ptrace",           0x004413    },
      [27]  = { "alarm",            0x000001    },
      [28]  = { "oldfstat",         0x000041    },
      [29]  = { "pause",            0xffffff    },
      [30]  = { "utime",            0x000045    },
      [31]  = { "stty" },
      [32]  = { "gtty" },
      [33]  = { "access",           0x000025    },
      [34]  = { "nice",       0x000001    },
      [35]  = { "ftime" },
      [36]  = { "sync",       0xffffff    },
      [37]  = { "kill",       0x000011    },
      [38]  = { "rename",           0x000055    },
      [39]  = { "mkdir",            0x000025    },
      [40]  = { "rmdir",            0x000005    },
      [41]  = { "dup",        0x000001    },
      [42]  = { "pipe",       0x000004    },
      [43]  = { "times",            0x000004    },
      [44]  = { "prof" },
      [45]  = { "brk",        0x000004    },
      [46]  = { "setgid",           0x000001    },
      [47]  = { "getgid",           0xffffff    },
      [48]  = { "signal",           0x000041    },
      [49]  = { "geteuid",          0xffffff    },
      [50]  = { "getegid",          0xffffff    },
      [51]  = { "acct",       0x000005    },
      [52]  = { "umount2",          0x000035    },
      [53]  = { "lock" },
      [54]  = { "ioctl",            0x000331    },
      [55]  = { "fcntl",            0x000331    },
      [56]  = { "mpx" },
      [57]  = { "setpgid",          0x000011    },
      [58]  = { "ulimit" },
      [60]  = { "umask",            0x000002    },
      [61]  = { "chroot",           0x000005    },
      [62]  = { "ustat",            0x000043    },
      [63]  = { "dup2",       0x000011    },
      [64]  = { "getppid",          0xffffff    },
      [65]  = { "getpgrp",          0xffffff    },
      [66]  = { "setsid",           0xffffff    },
      [67]  = { "sigaction" },
      [68]  = { "sgetmask" },
      [69]  = { "ssetmask" },
      [70]  = { "setreuid" },
      [71]  = { "setregid" },
      [72]  = { "sigsuspend" },
      [73]  = { "sigpending" },
      [74]  = { "sethostname" },
      [75]  = { "setrlimit" },
      [76]  = { "getrlimit" },
      [77]  = { "getrusage" },
      [78]  = { "gettimeofday" },
      [79]  = { "settimeofday" },
      [80]  = { "getgroups" },
      [81]  = { "setgroups" },
      [82]  = { "select" },
      [83]  = { "symlink" },
      [84]  = { "oldlstat" },
      [85]  = { "readlink" },
      [86]  = { "uselib" },
      [87]  = { "swapon" },
      [88]  = { "reboot" },
      [89]  = { "readdir" },
      [91]  = { "munmap",           0x000034    },
      [92]  = { "truncate" },
      [93]  = { "ftruncate" },
      [94]  = { "fchmod" },
      [95]  = { "fchown" },
      [96]  = { "getpriority" },
      [97]  = { "setpriority" },
      [99]  = { "statfs" },
      [100] = { "fstatfs" },
      [102] = { "socketcall" },
      [103] = { "syslog" },
      [104] = { "setitimer" },
      [105] = { "getitimer" },
      [106] = { "stat" },
      [107] = { "lstat" },
      [108] = { "fstat" },
      [111] = { "vhangup" },
      [114] = { "wait4" },
      [115] = { "swapoff" },
      [116] = { "sysinfo" },
      [117] = { "ipc" },
      [118] = { "fsync" },
      [119] = { "sigreturn" },
      [120] = { "clone" },
      [121] = { "setdomainname" },
      [122] = { "uname" },
      [123] = { "modify_ldt" },
      [123] = { "cacheflush" },
      [124] = { "adjtimex" },
      [125] = { "mprotect" },
      [126] = { "sigprocmask" },
      [127] = { "create_module" },
      [128] = { "init_module" },
      [129] = { "delete_module" },
      [130] = { "get_kernel_syms" },
      [131] = { "quotactl" },
      [132] = { "getpgid" },
      [133] = { "fchdir" },
      [134] = { "bdflush" },
      [135] = { "sysfs" },
      [136] = { "personality" },
      [137] = { "afs_syscall" },
      [138] = { "setfsuid" },
      [139] = { "setfsgid" },
      [140] = { "_llseek",                0x014331    },
      [141] = { "getdents" },
      [142] = { "_newselect",       0x000141    },
      [143] = { "flock" },
      [144] = { "msync" },
      [145] = { "readv" },
      [146] = { "writev" },
      [147] = { "getsid",                 0x000001    },
      [148] = { "fdatasync",        0x000001    },
      [149] = { "_sysctl",                0x000004    },
      [150] = { "mlock" },
      [151] = { "munlock" },
      [152] = { "mlockall" },
      [153] = { "munlockall" },
      [154] = { "sched_setparam" },
      [155] = { "sched_getparam" },
      [156] = { "sched_setscheduler" },
      [157] = { "sched_getscheduler" },
      [158] = { "sched_yield" },
      [159] = { "sched_get_priority_max" },
      [160] = { "sched_get_priority_min" },
      [161] = { "sched_rr_get_interval" },
      [162] = { "nanosleep",        0x000044    },
      [163] = { "mremap" },
      [164] = { "setresuid" },
      [165] = { "getresuid" },
      [166] = { "vm86" },
      [167] = { "query_module" },
      [168] = { "poll" },
      [169] = { "nfsservctl" },
      [170] = { "setresgid" },
      [171] = { "getresgid" },
      [172] = { "prctl",                  0x333331    },
      [173] = { "rt_sigreturn",           0xffffff    },
      [174] = { "rt_sigaction",           0x001441    },
      [175] = { "rt_sigprocmask",         0x001441    },
      [176] = { "rt_sigpending",          0x000014    },
      [177] = { "rt_sigtimedwait",        0x001444    },
      [178] = { "rt_sigqueueinfo",        0x000411    },
      [179] = { "rt_sigsuspend",          0x000014    },
      [180] = { "pread",                  0x003341    },
      [181] = { "pwrite",                 0x003341    },
      [182] = { "chown",                  0x000115    },
      [183] = { "getcwd" },
      [184] = { "capget" },
      [185] = { "capset" },
      [186] = { "sigaltstack" },
      [187] = { "sendfile" },
      [188] = { "getpmsg" },
      [189] = { "putpmsg" },
      [190] = { "vfork",                  0xffffff    },
      [191] = { "ugetrlimit" },
      [192] = { "mmap2",                  0x313314    },
      [193] = { "truncate64" },
      [194] = { "ftruncate64" },
      [195] = { "stat64",                 0x000045    },
      [196] = { "lstat64",                0x000045    },
      [197] = { "fstat64",                0x000041    },
      [198] = { "lchown32" },
      [199] = { "getuid32",               0xffffff    },
      [200] = { "getgid32",               0xffffff    },
      [201] = { "geteuid32",        0xffffff    },
      [202] = { "getegid32",        0xffffff    },
      [203] = { "setreuid32" },
      [204] = { "setregid32" },
      [205] = { "getgroups32" },
      [206] = { "setgroups32" },
      [207] = { "fchown32" },
      [208] = { "setresuid32" },
      [209] = { "getresuid32" },
      [210] = { "setresgid32" },
      [211] = { "getresgid32" },
      [212] = { "chown32" },
      [213] = { "setuid32" },
      [214] = { "setgid32" },
      [215] = { "setfsuid32" },
      [216] = { "setfsgid32" },
      [217] = { "pivot_root" },
      [218] = { "mincore" },
      [219] = { "madvise" },
      [220] = { "getdents64" },
      [221] = { "fcntl64" },
      [223] = { "security" },
      [224] = { "gettid" },
      [225] = { "readahead" },
      [226] = { "setxattr" },
      [227] = { "lsetxattr" },
      [228] = { "fsetxattr" },
      [229] = { "getxattr" },
      [230] = { "lgetxattr" },
      [231] = { "fgetxattr" },
      [232] = { "listxattr" },
      [233] = { "llistxattr" },
      [234] = { "flistxattr" },
      [235] = { "removexattr" },
      [236] = { "lremovexattr" },
      [237] = { "fremovexattr" },
      [238] = { "tkill" },
      [239] = { "sendfile64" },
      [240] = { "futex" },
      [241] = { "sched_setaffinity" },
      [242] = { "sched_getaffinity" },
      [243] = { "set_thread_area" },
      [244] = { "get_thread_area" },
      [245] = { "io_setup" },
      [246] = { "io_destroy" },
      [247] = { "io_getevents" },
      [248] = { "io_submit" },
      [249] = { "io_cancel" },
      [250] = { "fadvise64" },
      [252] = { "exit_group",       0x000001    },
      [253] = { "lookup_dcookie" },
      [254] = { "epoll_create" },
      [255] = { "epoll_ctl" },
      [256] = { "epoll_wait" },
      [257] = { "remap_file_pages" },
      [258] = { "set_tid_address" },
      [259] = { "timer_create" },
      [260] = { "timer_settime" },
      [261] = { "timer_gettime" },
      [262] = { "timer_getoverrun" },
      [263] = { "timer_delete" },
      [264] = { "clock_settime" },
      [265] = { "clock_gettime" },
      [266] = { "clock_getres" },
      [267] = { "clock_nanosleep" },
      [268] = { "statfs64" },
      [269] = { "fstatfs64" },
      [270] = { "tgkill" },
      [271] = { "utimes" },
      [272] = { "fadvise64_64" },
      [273] = { "vserver" },
      [274] = { "mbind" },
      [275] = { "get_mempolicy" },
      [276] = { "set_mempolicy" },
      [277] = { "mq_open" },
      [278] = { "mq_unlink" },
      [279] = { "mq_timedsend" },
      [280] = { "mq_timedreceive" },
      [281] = { "mq_notify" },
      [282] = { "mq_getsetattr" },
      [283] = { "sys_kexec_load" },
};

asmlinkage void do_syscall_trace(int leaving)
{
#if 0
      unsigned long *argp;
      const char *name;
      unsigned argmask;
      char buffer[16];

      if (!kstrace)
            return;

      if (!current->mm)
            return;

      if (__frame->gr7 == __NR_close)
            return;

#if 0
      if (__frame->gr7 != __NR_mmap2 &&
          __frame->gr7 != __NR_vfork &&
          __frame->gr7 != __NR_execve &&
          __frame->gr7 != __NR_exit)
            return;
#endif

      argmask = 0;
      name = NULL;
      if (__frame->gr7 < NR_syscalls) {
            name = __syscall_name_table[__frame->gr7].name;
            argmask = __syscall_name_table[__frame->gr7].argmask;
      }
      if (!name) {
            sprintf(buffer, "sys_%lx", __frame->gr7);
            name = buffer;
      }

      if (!leaving) {
            if (!argmask) {
                  printk(KERN_CRIT "[%d] %s(%lx,%lx,%lx,%lx,%lx,%lx)\n",
                         current->pid,
                         name,
                         __frame->gr8,
                         __frame->gr9,
                         __frame->gr10,
                         __frame->gr11,
                         __frame->gr12,
                         __frame->gr13);
            }
            else if (argmask == 0xffffff) {
                  printk(KERN_CRIT "[%d] %s()\n",
                         current->pid,
                         name);
            }
            else {
                  printk(KERN_CRIT "[%d] %s(",
                         current->pid,
                         name);

                  argp = &__frame->gr8;

                  do {
                        switch (argmask & 0xf) {
                        case 1:
                              printk("%ld", (long) *argp);
                              break;
                        case 2:
                              printk("%lo", *argp);
                              break;
                        case 3:
                              printk("%lx", *argp);
                              break;
                        case 4:
                              printk("%p", (void *) *argp);
                              break;
                        case 5:
                              printk("\"%s\"", (char *) *argp);
                              break;
                        }

                        argp++;
                        argmask >>= 4;
                        if (argmask)
                              printk(",");

                  } while (argmask);

                  printk(")\n");
            }
      }
      else {
            if ((int)__frame->gr8 > -4096 && (int)__frame->gr8 < 4096)
                  printk(KERN_CRIT "[%d] %s() = %ld\n", current->pid, name, __frame->gr8);
            else
                  printk(KERN_CRIT "[%d] %s() = %lx\n", current->pid, name, __frame->gr8);
      }
      return;
#endif

      if (!test_thread_flag(TIF_SYSCALL_TRACE))
            return;

      if (!(current->ptrace & PT_PTRACED))
            return;

      /* we need to indicate entry or exit to strace */
      if (leaving)
            __frame->__status |= REG__STATUS_SYSC_EXIT;
      else
            __frame->__status |= REG__STATUS_SYSC_ENTRY;

      ptrace_notify(SIGTRAP);

      /*
       * this isn't the same as continuing with a signal, but it will do
       * for normal use.  strace only continues with a signal if the
       * stopping signal is not SIGTRAP.  -brl
       */
      if (current->exit_code) {
            send_sig(current->exit_code, current, 1);
            current->exit_code = 0;
      }
}

Generated by  Doxygen 1.6.0   Back to index