📄 ptrace.c
字号:
/* * linux/arch/m32r/kernel/ptrace.c * * Copyright (C) 2002 Hirokazu Takata, Takeo Takahashi * Copyright (C) 2004 Hirokazu Takata, Kei Sakamoto * * Original x86 implementation: * By Ross Biro 1/23/92 * edited by Linus Torvalds * * Some code taken from sh version: * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka * Some code taken from arm version: * Copyright (C) 2000 Russell King */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/ptrace.h>#include <linux/user.h>#include <linux/string.h>#include <linux/signal.h>#include <asm/cacheflush.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/processor.h>#include <asm/mmu_context.h>/* * Get the address of the live pt_regs for the specified task. * These are saved onto the top kernel stack when the process * is not running. * * Note: if a user thread is execve'd from kernel space, the * kernel stack will not be empty on entry to the kernel, so * ptracing these tasks will fail. */static inline struct pt_regs *get_user_regs(struct task_struct *task){ return (struct pt_regs *) ((unsigned long)task->thread_info + THREAD_SIZE - sizeof(struct pt_regs));}/* * This routine will get a word off of the process kernel stack. */static inline unsigned long intget_stack_long(struct task_struct *task, int offset){ unsigned long *stack; stack = (unsigned long *)get_user_regs(task); return stack[offset];}/* * This routine will put a word on the process kernel stack. */static inline intput_stack_long(struct task_struct *task, int offset, unsigned long data){ unsigned long *stack; stack = (unsigned long *)get_user_regs(task); stack[offset] = data; return 0;}static int reg_offset[] = { PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7, PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_FP, PT_LR, PT_SPU,};/* * Read the word at offset "off" into the "struct user". We * actually access the pt_regs stored on the kernel stack. */static int ptrace_read_user(struct task_struct *tsk, unsigned long off, unsigned long __user *data){ unsigned long tmp;#ifndef NO_FPU struct user * dummy = NULL;#endif if ((off & 3) || (off < 0) || (off > sizeof(struct user) - 3)) return -EIO; off >>= 2; switch (off) { case PT_EVB: __asm__ __volatile__ ( "mvfc %0, cr5 \n\t" : "=r" (tmp) ); break; case PT_CBR: { unsigned long psw; psw = get_stack_long(tsk, PT_PSW); tmp = ((psw >> 8) & 1); } break; case PT_PSW: { unsigned long psw, bbpsw; psw = get_stack_long(tsk, PT_PSW); bbpsw = get_stack_long(tsk, PT_BBPSW); tmp = ((psw >> 8) & 0xff) | ((bbpsw & 0xff) << 8); } break; case PT_PC: tmp = get_stack_long(tsk, PT_BPC); break; case PT_BPC: off = PT_BBPC; /* fall through */ default: if (off < (sizeof(struct pt_regs) >> 2)) tmp = get_stack_long(tsk, off);#ifndef NO_FPU else if (off >= (long)(&dummy->fpu >> 2) && off < (long)(&dummy->u_fpvalid >> 2)) { if (!tsk_used_math(tsk)) { if (off == (long)(&dummy->fpu.fpscr >> 2)) tmp = FPSCR_INIT; else tmp = 0; } else tmp = ((long *)(&tsk->thread.fpu >> 2)) [off - (long)&dummy->fpu]; } else if (off == (long)(&dummy->u_fpvalid >> 2)) tmp = !!tsk_used_math(tsk);#endif /* not NO_FPU */ else tmp = 0; } return put_user(tmp, data);}static int ptrace_write_user(struct task_struct *tsk, unsigned long off, unsigned long data){ int ret = -EIO;#ifndef NO_FPU struct user * dummy = NULL;#endif if ((off & 3) || off < 0 || off > sizeof(struct user) - 3) return -EIO; off >>= 2; switch (off) { case PT_EVB: case PT_BPC: case PT_SPI: /* We don't allow to modify evb. */ ret = 0; break; case PT_PSW: case PT_CBR: { /* We allow to modify only cbr in psw */ unsigned long psw; psw = get_stack_long(tsk, PT_PSW); psw = (psw & ~0x100) | ((data & 1) << 8); ret = put_stack_long(tsk, PT_PSW, psw); } break; case PT_PC: off = PT_BPC; data &= ~1; /* fall through */ default: if (off < (sizeof(struct pt_regs) >> 2)) ret = put_stack_long(tsk, off, data);#ifndef NO_FPU else if (off >= (long)(&dummy->fpu >> 2) && off < (long)(&dummy->u_fpvalid >> 2)) { set_stopped_child_used_math(tsk); ((long *)&tsk->thread.fpu) [off - (long)&dummy->fpu] = data; ret = 0; } else if (off == (long)(&dummy->u_fpvalid >> 2)) { conditional_stopped_child_used_math(data, tsk); ret = 0; }#endif /* not NO_FPU */ break; } return ret;}/* * Get all user integer registers. */static int ptrace_getregs(struct task_struct *tsk, void __user *uregs){ struct pt_regs *regs = get_user_regs(tsk); return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;}/* * Set all user integer registers. */static int ptrace_setregs(struct task_struct *tsk, void __user *uregs){ struct pt_regs newregs; int ret; ret = -EFAULT; if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) { struct pt_regs *regs = get_user_regs(tsk); *regs = newregs; ret = 0; } return ret;}static inline intcheck_condition_bit(struct task_struct *child){ return (int)((get_stack_long(child, PT_PSW) >> 8) & 1);}static intcheck_condition_src(unsigned long op, unsigned long regno1, unsigned long regno2, struct task_struct *child){ unsigned long reg1, reg2; reg2 = get_stack_long(child, reg_offset[regno2]); switch (op) { case 0x0: /* BEQ */ reg1 = get_stack_long(child, reg_offset[regno1]); return reg1 == reg2; case 0x1: /* BNE */ reg1 = get_stack_long(child, reg_offset[regno1]); return reg1 != reg2; case 0x8: /* BEQZ */ return reg2 == 0; case 0x9: /* BNEZ */ return reg2 != 0; case 0xa: /* BLTZ */ return (int)reg2 < 0; case 0xb: /* BGEZ */ return (int)reg2 >= 0; case 0xc: /* BLEZ */ return (int)reg2 <= 0; case 0xd: /* BGTZ */ return (int)reg2 > 0; default: /* never reached */ return 0; }}static voidcompute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, unsigned long *next_pc, struct task_struct *child){ unsigned long op, op2, op3; unsigned long disp; unsigned long regno; int parallel = 0; if (insn & 0x00008000) parallel = 1; if (pc & 3) insn &= 0x7fff; /* right slot */ else insn >>= 16; /* left slot */ op = (insn >> 12) & 0xf; op2 = (insn >> 8) & 0xf; op3 = (insn >> 4) & 0xf; if (op == 0x7) { switch (op2) { case 0xd: /* BNC */ case 0x9: /* BNCL */ if (!check_condition_bit(child)) { disp = (long)(insn << 24) >> 22; *next_pc = (pc & ~0x3) + disp; return; } break; case 0x8: /* BCL */ case 0xc: /* BC */ if (check_condition_bit(child)) { disp = (long)(insn << 24) >> 22; *next_pc = (pc & ~0x3) + disp; return; } break; case 0xe: /* BL */ case 0xf: /* BRA */ disp = (long)(insn << 24) >> 22; *next_pc = (pc & ~0x3) + disp; return; break; } } else if (op == 0x1) { switch (op2) { case 0x0: if (op3 == 0xf) { /* TRAP */#if 1 /* pass through */#else /* kernel space is not allowed as next_pc */ unsigned long evb; unsigned long trapno; trapno = insn & 0xf; __asm__ __volatile__ ( "mvfc %0, cr5\n" :"=r"(evb) : ); *next_pc = evb + (trapno << 2); return;#endif } else if (op3 == 0xd) { /* RTE */ *next_pc = get_stack_long(child, PT_BPC); return; } break; case 0xc: /* JC */ if (op3 == 0xc && check_condition_bit(child)) { regno = insn & 0xf; *next_pc = get_stack_long(child, reg_offset[regno]); return; } break; case 0xd: /* JNC */ if (op3 == 0xc && !check_condition_bit(child)) { regno = insn & 0xf; *next_pc = get_stack_long(child, reg_offset[regno]); return; } break; case 0xe: /* JL */ case 0xf: /* JMP */ if (op3 == 0xc) { /* JMP */ regno = insn & 0xf; *next_pc = get_stack_long(child, reg_offset[regno]); return; } break; } } if (parallel) *next_pc = pc + 4; else *next_pc = pc + 2;}static voidcompute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc, unsigned long *next_pc, struct task_struct *child){ unsigned long op; unsigned long op2; unsigned long disp; unsigned long regno1, regno2; op = (insn >> 28) & 0xf; if (op == 0xf) { /* branch 24-bit relative */ op2 = (insn >> 24) & 0xf; switch (op2) { case 0xd: /* BNC */ case 0x9: /* BNCL */ if (!check_condition_bit(child)) { disp = (long)(insn << 8) >> 6; *next_pc = (pc & ~0x3) + disp; return; } break; case 0x8: /* BCL */ case 0xc: /* BC */ if (check_condition_bit(child)) { disp = (long)(insn << 8) >> 6; *next_pc = (pc & ~0x3) + disp; return; } break; case 0xe: /* BL */ case 0xf: /* BRA */ disp = (long)(insn << 8) >> 6; *next_pc = (pc & ~0x3) + disp; return; } } else if (op == 0xb) { /* branch 16-bit relative */ op2 = (insn >> 20) & 0xf; switch (op2) { case 0x0: /* BEQ */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -