ptrace.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:8k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/arch/cris/kernel/ptrace.c
  3.  *
  4.  * Parts taken from the m68k port.
  5.  * 
  6.  * Copyright (c) 2000, 2001 Axis Communications AB
  7.  *
  8.  * Authors:   Bjorn Wesen
  9.  *
  10.  * $Log: ptrace.c,v $
  11.  * Revision 1.8  2001/11/12 18:26:21  pkj
  12.  * Fixed compiler warnings.
  13.  *
  14.  * Revision 1.7  2001/09/26 11:53:49  bjornw
  15.  * PTRACE_DETACH works more simple in 2.4.10
  16.  *
  17.  * Revision 1.6  2001/07/25 16:08:47  bjornw
  18.  * PTRACE_ATTACH bulk moved into arch-independant code in 2.4.7
  19.  *
  20.  * Revision 1.5  2001/03/26 14:24:28  orjanf
  21.  * * Changed loop condition.
  22.  * * Added comment documenting non-standard ptrace behaviour.
  23.  *
  24.  * Revision 1.4  2001/03/20 19:44:41  bjornw
  25.  * Use the user_regs macro instead of thread.esp0
  26.  *
  27.  * Revision 1.3  2000/12/18 23:45:25  bjornw
  28.  * Linux/CRIS first version
  29.  *
  30.  *
  31.  */
  32. #include <linux/kernel.h>
  33. #include <linux/sched.h>
  34. #include <linux/mm.h>
  35. #include <linux/smp.h>
  36. #include <linux/smp_lock.h>
  37. #include <linux/errno.h>
  38. #include <linux/ptrace.h>
  39. #include <linux/user.h>
  40. #include <asm/uaccess.h>
  41. #include <asm/page.h>
  42. #include <asm/pgtable.h>
  43. #include <asm/system.h>
  44. #include <asm/processor.h>
  45. /*
  46.  * does not yet catch signals sent when the child dies.
  47.  * in exit.c or in signal.c.
  48.  */
  49. /* determines which bits in DCCR the user has access to. */
  50. /* 1 = access 0 = no access */
  51. #define DCCR_MASK 0x0000001f     /* XNZVC */
  52. /*
  53.  * Get contents of register REGNO in task TASK.
  54.  */
  55. static inline long get_reg(struct task_struct *task, unsigned int regno)
  56. {
  57. /* USP is a special case, it's not in the pt_regs struct but
  58.  * in the tasks thread struct
  59.  */
  60. if (regno == PT_USP)
  61. return task->thread.usp;
  62. else if (regno < PT_MAX)
  63. return ((unsigned long *)user_regs(task))[regno];
  64. else
  65. return 0;
  66. }
  67. /*
  68.  * Write contents of register REGNO in task TASK.
  69.  */
  70. static inline int put_reg(struct task_struct *task, unsigned int regno,
  71.   unsigned long data)
  72. {
  73. if (regno == PT_USP)
  74. task->thread.usp = data;
  75. else if (regno < PT_MAX)
  76. ((unsigned long *)user_regs(task))[regno] = data;
  77. else
  78. return -1;
  79. return 0;
  80. }
  81. /*
  82.  * Called by kernel/ptrace.c when detaching..
  83.  *
  84.  * Make sure the single step bit is not set.
  85.  */
  86. void ptrace_disable(struct task_struct *child)
  87. {
  88.        /* Todo - pending singlesteps? */
  89. }
  90. /* Note that this implementation of ptrace behaves differently from vanilla
  91.  * ptrace.  Contrary to what the man page says, in the PTRACE_PEEKTEXT,
  92.  * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
  93.  * ignored.  Instead, the data variable is expected to point at a location
  94.  * (in user space) where the result of the ptrace call is written (instead of
  95.  * being returned).
  96.  */
  97. asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
  98. {
  99. struct task_struct *child;
  100. int ret;
  101. lock_kernel();
  102. ret = -EPERM;
  103. if (request == PTRACE_TRACEME) {
  104. /* are we already being traced? */
  105. if (current->ptrace & PT_PTRACED)
  106. goto out;
  107. /* set the ptrace bit in the process flags. */
  108. current->ptrace |= PT_PTRACED;
  109. ret = 0;
  110. goto out;
  111. }
  112. ret = -ESRCH;
  113. read_lock(&tasklist_lock);
  114. child = find_task_by_pid(pid);
  115. if (child)
  116. get_task_struct(child);
  117. read_unlock(&tasklist_lock);
  118. if (!child)
  119. goto out;
  120. ret = -EPERM;
  121. if (pid == 1) /* you may not mess with init */
  122. goto out_tsk;
  123. if (request == PTRACE_ATTACH) {
  124. ret = ptrace_attach(child);
  125. goto out_tsk;
  126. }
  127. ret = -ESRCH;
  128. if (!(child->ptrace & PT_PTRACED))
  129. goto out_tsk;
  130. if (child->state != TASK_STOPPED) {
  131. if (request != PTRACE_KILL)
  132. goto out_tsk;
  133. }
  134. if (child->p_pptr != current)
  135. goto out_tsk;
  136. switch (request) {
  137. /* when I and D space are separate, these will need to be fixed. */
  138. case PTRACE_PEEKTEXT: /* read word at location addr. */ 
  139. case PTRACE_PEEKDATA: {
  140. unsigned long tmp;
  141. int copied;
  142. copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
  143. ret = -EIO;
  144. if (copied != sizeof(tmp))
  145. break;
  146. ret = put_user(tmp,(unsigned long *) data);
  147. break;
  148. }
  149. /* read the word at location addr in the USER area. */
  150. case PTRACE_PEEKUSR: {
  151. unsigned long tmp;
  152. ret = -EIO;
  153. if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
  154. break;
  155. tmp = 0;  /* Default return condition */
  156. ret = -EIO;
  157. if (addr < sizeof(struct pt_regs)) {
  158. tmp = get_reg(child, addr >> 2);
  159. ret = put_user(tmp, (unsigned long *)data);
  160. }
  161. break;
  162. }
  163. /* when I and D space are separate, this will have to be fixed. */
  164. case PTRACE_POKETEXT: /* write the word at location addr. */
  165. case PTRACE_POKEDATA:
  166. ret = 0;
  167. if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
  168. break;
  169. ret = -EIO;
  170. break;
  171. case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
  172. ret = -EIO;
  173. if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
  174. break;
  175. if (addr < sizeof(struct pt_regs)) {
  176. addr >>= 2;
  177. if (addr == PT_DCCR) {
  178. /* don't allow the tracing process to change stuff like
  179.  * interrupt enable, kernel/user bit, dma enables etc.
  180.  */
  181. data &= DCCR_MASK;
  182. data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
  183. }
  184. if (put_reg(child, addr, data))
  185. break;
  186. ret = 0;
  187. }
  188. break;
  189. case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
  190. case PTRACE_CONT: /* restart after signal. */
  191. ret = -EIO;
  192. if ((unsigned long) data > _NSIG)
  193. break;
  194. if (request == PTRACE_SYSCALL)
  195. child->ptrace |= PT_TRACESYS;
  196. else
  197. child->ptrace &= ~PT_TRACESYS;
  198. child->exit_code = data;
  199. /* TODO: make sure any pending breakpoint is killed */
  200. wake_up_process(child);
  201. ret = 0;
  202. break;
  203. /*
  204.  * make the child exit.  Best I can do is send it a sigkill. 
  205.  * perhaps it should be put in the status that it wants to 
  206.  * exit.
  207.  */
  208. case PTRACE_KILL:
  209. ret = 0;
  210. if (child->state == TASK_ZOMBIE) /* already dead */
  211. break;
  212. child->exit_code = SIGKILL;
  213. /* TODO: make sure any pending breakpoint is killed */
  214. wake_up_process(child);
  215. break;
  216. case PTRACE_SINGLESTEP: /* set the trap flag. */
  217. ret = -EIO;
  218. if ((unsigned long) data > _NSIG)
  219. break;
  220. child->ptrace &= ~PT_TRACESYS;
  221. /* TODO: set some clever breakpoint mechanism... */
  222. child->exit_code = data;
  223. /* give it a chance to run. */
  224. wake_up_process(child);
  225. ret = 0;
  226. break;
  227. case PTRACE_DETACH:
  228. ret = ptrace_detach(child, data);
  229. break;
  230. case PTRACE_GETREGS: { /* Get all gp regs from the child. */
  231.    int i;
  232. unsigned long tmp;
  233. for (i = 0; i <= PT_MAX; i++) {
  234. tmp = get_reg(child, i);
  235. if (put_user(tmp, (unsigned long *) data)) {
  236. ret = -EFAULT;
  237. break;
  238. }
  239. data += sizeof(long);
  240. }
  241. ret = 0;
  242. break;
  243. }
  244. case PTRACE_SETREGS: { /* Set all gp regs in the child. */
  245. int i;
  246. unsigned long tmp;
  247. for (i = 0; i <= PT_MAX; i++) {
  248. if (get_user(tmp, (unsigned long *) data)) {
  249. ret = -EFAULT;
  250. break;
  251. }
  252. if (i == PT_DCCR) {
  253. tmp &= DCCR_MASK;
  254. tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
  255. }
  256. put_reg(child, i, tmp);
  257. data += sizeof(long);
  258. }
  259. ret = 0;
  260. break;
  261. }
  262. default:
  263. ret = -EIO;
  264. break;
  265. }
  266. out_tsk:
  267. free_task_struct(child);
  268. out:
  269. unlock_kernel();
  270. return ret;
  271. }
  272. asmlinkage void syscall_trace(void)
  273. {
  274. if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) !=
  275.     (PT_PTRACED | PT_TRACESYS))
  276. return;
  277. /* TODO: make a way to distinguish between a syscall stop and SIGTRAP
  278.  * delivery like in the i386 port ? 
  279.  */
  280. current->exit_code = SIGTRAP;
  281. current->state = TASK_STOPPED;
  282. notify_parent(current, SIGCHLD);
  283. schedule();
  284. /*
  285.  * this isn't the same as continuing with a signal, but it will do
  286.  * for normal use.  strace only continues with a signal if the
  287.  * stopping signal is not SIGTRAP.  -brl
  288.  */
  289. if (current->exit_code) {
  290. send_sig(current->exit_code, current, 1);
  291. current->exit_code = 0;
  292. }
  293. }