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

嵌入式Linux

开发平台:

Unix_Linux

  1. /* $Id: ptrace.c,v 1.13 2001/10/01 02:21:50 gniibe Exp $
  2.  *
  3.  * linux/arch/sh/kernel/ptrace.c
  4.  *
  5.  * Original x86 implementation:
  6.  * By Ross Biro 1/23/92
  7.  * edited by Linus Torvalds
  8.  *
  9.  * SuperH version:   Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
  10.  *
  11.  */
  12. #include <linux/config.h>
  13. #include <linux/kernel.h>
  14. #include <linux/sched.h>
  15. #include <linux/mm.h>
  16. #include <linux/smp.h>
  17. #include <linux/smp_lock.h>
  18. #include <linux/errno.h>
  19. #include <linux/ptrace.h>
  20. #include <linux/user.h>
  21. #include <asm/io.h>
  22. #include <asm/uaccess.h>
  23. #include <asm/pgtable.h>
  24. #include <asm/system.h>
  25. #include <asm/processor.h>
  26. #include <asm/mmu_context.h>
  27. /*
  28.  * does not yet catch signals sent when the child dies.
  29.  * in exit.c or in signal.c.
  30.  */
  31. /*
  32.  * This routine will get a word off of the process kernel stack.
  33.  */
  34. static inline int get_stack_long(struct task_struct *task, int offset)
  35. {
  36. unsigned char *stack;
  37. stack = (unsigned char *)task + THREAD_SIZE - sizeof(struct pt_regs);
  38. stack += offset;
  39. return (*((int *)stack));
  40. }
  41. /*
  42.  * This routine will put a word on the process kernel stack.
  43.  */
  44. static inline int put_stack_long(struct task_struct *task, int offset,
  45.  unsigned long data)
  46. {
  47. unsigned char *stack;
  48. stack = (unsigned char *)task + THREAD_SIZE - sizeof(struct pt_regs);
  49. stack += offset;
  50. *(unsigned long *) stack = data;
  51. return 0;
  52. }
  53. static void
  54. compute_next_pc(struct pt_regs *regs, unsigned short inst,
  55. unsigned long *pc1, unsigned long *pc2)
  56. {
  57. int nib[4]
  58. = { (inst >> 12) & 0xf,
  59.     (inst >> 8) & 0xf,
  60.     (inst >> 4) & 0xf,
  61.     inst & 0xf};
  62. /* bra & bsr */
  63. if (nib[0] == 0xa || nib[0] == 0xb) {
  64. *pc1 = regs->pc + 4 + ((short) ((inst & 0xfff) << 4) >> 3);
  65. *pc2 = (unsigned long) -1;
  66. return;
  67. }
  68. /* bt & bf */
  69. if (nib[0] == 0x8 && (nib[1] == 0x9 || nib[1] == 0xb)) {
  70. *pc1 = regs->pc + 4 + ((char) (inst & 0xff) << 1);
  71. *pc2 = regs->pc + 2;
  72. return;
  73. }
  74. /* bt/s & bf/s */
  75. if (nib[0] == 0x8 && (nib[1] == 0xd || nib[1] == 0xf)) {
  76. *pc1 = regs->pc + 4 + ((char) (inst & 0xff) << 1);
  77. *pc2 = regs->pc + 4;
  78. return;
  79. }
  80. /* jmp & jsr */
  81. if (nib[0] == 0x4 && nib[3] == 0xb
  82.     && (nib[2] == 0x0 || nib[2] == 0x2)) {
  83. *pc1 = regs->regs[nib[1]];
  84. *pc2 = (unsigned long) -1;
  85. return;
  86. }
  87. /* braf & bsrf */
  88. if (nib[0] == 0x0 && nib[3] == 0x3
  89.     && (nib[2] == 0x0 || nib[2] == 0x2)) {
  90. *pc1 = regs->pc + 4 + regs->regs[nib[1]];
  91. *pc2 = (unsigned long) -1;
  92. return;
  93. }
  94. if (inst == 0x000b) {
  95. *pc1 = regs->pr;
  96. *pc2 = (unsigned long) -1;
  97. return;
  98. }
  99. *pc1 = regs->pc + 2;
  100. *pc2 = (unsigned long) -1;
  101. return;
  102. }
  103. /* Tracing by user break controller.  */
  104. static void
  105. ubc_set_tracing(int asid, unsigned long nextpc1, unsigned nextpc2)
  106. {
  107. ctrl_outl(nextpc1, UBC_BARA);
  108. ctrl_outb(asid, UBC_BASRA);
  109. if(UBC_TYPE_SH7729){
  110. ctrl_outl(0x0fff, UBC_BAMRA);
  111. ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRA);
  112. }else{
  113. ctrl_outb(BAMR_12, UBC_BAMRA);
  114. ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA);
  115. }
  116. if (nextpc2 != (unsigned long) -1) {
  117. ctrl_outl(nextpc2, UBC_BARB);
  118. ctrl_outb(asid, UBC_BASRB);
  119. if(UBC_TYPE_SH7729){
  120. ctrl_outl(0x0fff, UBC_BAMRB);
  121. ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRB);
  122. }else{
  123. ctrl_outb(BAMR_12, UBC_BAMRB);
  124. ctrl_outw(BBR_INST | BBR_READ, UBC_BBRB);
  125. }
  126. }
  127. if(UBC_TYPE_SH7729)
  128. ctrl_outl(BRCR_PCBA | BRCR_PCBB | BRCR_PCTE, UBC_BRCR);
  129. else
  130. ctrl_outw(BRCR_PCBA | BRCR_PCBB, UBC_BRCR);
  131. }
  132. /*
  133.  * Called by kernel/ptrace.c when detaching..
  134.  *
  135.  * Make sure single step bits etc are not set.
  136.  */
  137. void ptrace_disable(struct task_struct *child)
  138. {
  139. /* nothing to do.. */
  140. }
  141. asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
  142. {
  143. struct task_struct *child, *tsk = current;
  144. struct user * dummy = NULL;
  145. int ret;
  146. lock_kernel();
  147. ret = -EPERM;
  148. if (request == PTRACE_TRACEME) {
  149. /* are we already being traced? */
  150. if (current->ptrace & PT_PTRACED)
  151. goto out;
  152. /* set the ptrace bit in the process flags. */
  153. current->ptrace |= PT_PTRACED;
  154. ret = 0;
  155. goto out;
  156. }
  157. ret = -ESRCH;
  158. read_lock(&tasklist_lock);
  159. child = find_task_by_pid(pid);
  160. if (child)
  161. get_task_struct(child);
  162. read_unlock(&tasklist_lock);
  163. if (!child)
  164. goto out;
  165. ret = -EPERM;
  166. if (pid == 1) /* you may not mess with init */
  167. goto out_tsk;
  168. if (request == PTRACE_ATTACH) {
  169. ret = ptrace_attach(child);
  170. goto out_tsk;
  171. }
  172. ret = -ESRCH;
  173. if (!(child->ptrace & PT_PTRACED))
  174. goto out_tsk;
  175. if (child->state != TASK_STOPPED) {
  176. if (request != PTRACE_KILL)
  177. goto out_tsk;
  178. }
  179. if (child->p_pptr != tsk)
  180. goto out_tsk;
  181. switch (request) {
  182. /* when I and D space are separate, these will need to be fixed. */
  183. case PTRACE_PEEKTEXT: /* read word at location addr. */ 
  184. case PTRACE_PEEKDATA: {
  185. unsigned long tmp;
  186. int copied;
  187. copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
  188. ret = -EIO;
  189. if (copied != sizeof(tmp))
  190. break;
  191. ret = put_user(tmp,(unsigned long *) data);
  192. break;
  193. }
  194. /* read the word at location addr in the USER area. */
  195. case PTRACE_PEEKUSR: {
  196. unsigned long tmp;
  197. ret = -EIO;
  198. if ((addr & 3) || addr < 0 || 
  199.     addr > sizeof(struct user) - 3)
  200. break;
  201. if (addr < sizeof(struct pt_regs))
  202. tmp = get_stack_long(child, addr);
  203. else if (addr >= (long) &dummy->fpu &&
  204.  addr < (long) &dummy->u_fpvalid) {
  205. if (!child->used_math) {
  206. if (addr == (long)&dummy->fpu.fpscr)
  207. tmp = FPSCR_INIT;
  208. else
  209. tmp = 0;
  210. } else
  211. tmp = ((long *)&child->thread.fpu)
  212. [(addr - (long)&dummy->fpu) >> 2];
  213. } else if (addr == (long) &dummy->u_fpvalid)
  214. tmp = child->used_math;
  215. else
  216. tmp = 0;
  217. ret = put_user(tmp, (unsigned long *)data);
  218. break;
  219. }
  220. /* when I and D space are separate, this will have to be fixed. */
  221. case PTRACE_POKETEXT: /* write the word at location addr. */
  222. case PTRACE_POKEDATA:
  223. ret = 0;
  224. if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
  225. break;
  226. ret = -EIO;
  227. break;
  228. case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
  229. ret = -EIO;
  230. if ((addr & 3) || addr < 0 || 
  231.     addr > sizeof(struct user) - 3)
  232. break;
  233. if (addr < sizeof(struct pt_regs))
  234. ret = put_stack_long(child, addr, data);
  235. else if (addr >= (long) &dummy->fpu &&
  236.  addr < (long) &dummy->u_fpvalid) {
  237. child->used_math = 1;
  238. ((long *)&child->thread.fpu)
  239. [(addr - (long)&dummy->fpu) >> 2] = data;
  240. ret = 0;
  241. } else if (addr == (long) &dummy->u_fpvalid) {
  242. child->used_math = data?1:0;
  243. ret = 0;
  244. }
  245. break;
  246. case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
  247. case PTRACE_CONT: { /* restart after signal. */
  248. ret = -EIO;
  249. if ((unsigned long) data > _NSIG)
  250. break;
  251. if (request == PTRACE_SYSCALL)
  252. child->ptrace |= PT_TRACESYS;
  253. else
  254. child->ptrace &= ~PT_TRACESYS;
  255. child->exit_code = data;
  256. wake_up_process(child);
  257. ret = 0;
  258. break;
  259. }
  260. /*
  261.  * make the child exit.  Best I can do is send it a sigkill. 
  262.  * perhaps it should be put in the status that it wants to 
  263.  * exit.
  264.  */
  265. case PTRACE_KILL: {
  266. ret = 0;
  267. if (child->state == TASK_ZOMBIE) /* already dead */
  268. break;
  269. child->exit_code = SIGKILL;
  270. wake_up_process(child);
  271. break;
  272. }
  273. case PTRACE_SINGLESTEP: {  /* set the trap flag. */
  274. long tmp, pc;
  275. struct pt_regs *dummy = NULL;
  276. struct pt_regs *regs;
  277. unsigned long nextpc1, nextpc2;
  278. unsigned short insn;
  279. ret = -EIO;
  280. if ((unsigned long) data > _NSIG)
  281. break;
  282. child->ptrace &= ~PT_TRACESYS;
  283. if ((child->ptrace & PT_DTRACE) == 0) {
  284. /* Spurious delayed TF traps may occur */
  285. child->ptrace |= PT_DTRACE;
  286. }
  287. /* Compute next pc.  */
  288. pc = get_stack_long(child, (long)&dummy->pc);
  289. regs = (struct pt_regs *)((unsigned long)child + THREAD_SIZE - sizeof(struct pt_regs));
  290. if (access_process_vm(child, pc&~3, &tmp, sizeof(tmp), 0) != sizeof(tmp))
  291. break;
  292.  
  293. #ifdef  __LITTLE_ENDIAN__
  294. if (pc & 3)
  295. insn = tmp >> 16;
  296. else
  297. insn = tmp & 0xffff;
  298. #else
  299. if (pc & 3)
  300. insn = tmp & 0xffff;
  301. else
  302. insn = tmp >> 16;
  303. #endif
  304. compute_next_pc (regs, insn, &nextpc1, &nextpc2);
  305. if (nextpc1 & 0x80000000)
  306. break;
  307. if (nextpc2 != (unsigned long) -1 && (nextpc2 & 0x80000000))
  308. break;
  309. ubc_set_tracing(child->mm->context & MMU_CONTEXT_ASID_MASK,
  310. nextpc1, nextpc2);
  311. child->exit_code = data;
  312. /* give it a chance to run. */
  313. wake_up_process(child);
  314. ret = 0;
  315. break;
  316. }
  317. case PTRACE_DETACH: /* detach a process that was attached. */
  318. ret = ptrace_detach(child, data);
  319. break;
  320. case PTRACE_SETOPTIONS: {
  321. if (data & PTRACE_O_TRACESYSGOOD)
  322. child->ptrace |= PT_TRACESYSGOOD;
  323. else
  324. child->ptrace &= ~PT_TRACESYSGOOD;
  325. ret = 0;
  326. break;
  327. }
  328. default:
  329. ret = -EIO;
  330. break;
  331. }
  332. out_tsk:
  333. free_task_struct(child);
  334. out:
  335. unlock_kernel();
  336. return ret;
  337. }
  338. asmlinkage void syscall_trace(void)
  339. {
  340. struct task_struct *tsk = current;
  341. if ((tsk->ptrace & (PT_PTRACED|PT_TRACESYS))
  342.     != (PT_PTRACED|PT_TRACESYS))
  343. return;
  344. /* the 0x80 provides a way for the tracing parent to distinguish
  345.    between a syscall stop and SIGTRAP delivery */
  346. tsk->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
  347.     ? 0x80 : 0);
  348. tsk->state = TASK_STOPPED;
  349. notify_parent(tsk, SIGCHLD);
  350. schedule();
  351. /*
  352.  * this isn't the same as continuing with a signal, but it will do
  353.  * for normal use.  strace only continues with a signal if the
  354.  * stopping signal is not SIGTRAP.  -brl
  355.  */
  356. if (tsk->exit_code) {
  357. send_sig(tsk->exit_code, tsk, 1);
  358. tsk->exit_code = 0;
  359. }
  360. }