📄 ptrace.c
字号:
/* * linux/arch/arm26/kernel/ptrace.c * * By Ross Biro 1/23/92 * edited by Linus Torvalds * ARM modifications Copyright (C) 2000 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#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/ptrace.h>#include <linux/user.h>#include <linux/security.h>#include <linux/signal.h>#include <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/system.h>//#include <asm/processor.h>#include "ptrace.h"#define REG_PC 15#define REG_PSR 15/* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. *//* * Breakpoint SWI instruction: SWI &9F0001 */#define BREAKINST_ARM 0xef9f0001/* * 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 __get_user_regs(task->thread_info);}/* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the THREAD. * this routine assumes that all the privileged stacks are in our * data space. */static inline long get_user_reg(struct task_struct *task, int offset){ return get_user_regs(task)->uregs[offset];}/* * this routine will put a word on the processes privileged stack. * the offset is how far from the base addr as stored in the THREAD. * this routine assumes that all the privileged stacks are in our * data space. */static inline intput_user_reg(struct task_struct *task, int offset, long data){ struct pt_regs newregs, *regs = get_user_regs(task); int ret = -EINVAL; newregs = *regs; newregs.uregs[offset] = data; if (valid_user_regs(&newregs)) { regs->uregs[offset] = data; ret = 0; } return ret;}static inline intread_u32(struct task_struct *task, unsigned long addr, u32 *res){ int ret; ret = access_process_vm(task, addr, res, sizeof(*res), 0); return ret == sizeof(*res) ? 0 : -EIO;}static inline intread_instr(struct task_struct *task, unsigned long addr, u32 *res){ int ret; u32 val; ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0); ret = ret == sizeof(val) ? 0 : -EIO; *res = val; return ret;}/* * Get value of register `rn' (in the instruction) */static unsigned longptrace_getrn(struct task_struct *child, unsigned long insn){ unsigned int reg = (insn >> 16) & 15; unsigned long val; val = get_user_reg(child, reg); if (reg == 15) val = pc_pointer(val + 8); //FIXME - correct for arm26? return val;}/* * Get value of operand 2 (in an ALU instruction) */static unsigned longptrace_getaluop2(struct task_struct *child, unsigned long insn){ unsigned long val; int shift; int type; if (insn & 1 << 25) { val = insn & 255; shift = (insn >> 8) & 15; type = 3; } else { val = get_user_reg (child, insn & 15); if (insn & (1 << 4)) shift = (int)get_user_reg (child, (insn >> 8) & 15); else shift = (insn >> 7) & 31; type = (insn >> 5) & 3; } switch (type) { case 0: val <<= shift; break; case 1: val >>= shift; break; case 2: val = (((signed long)val) >> shift); break; case 3: val = (val >> shift) | (val << (32 - shift)); break; } return val;}/* * Get value of operand 2 (in a LDR instruction) */static unsigned longptrace_getldrop2(struct task_struct *child, unsigned long insn){ unsigned long val; int shift; int type; val = get_user_reg(child, insn & 15); shift = (insn >> 7) & 31; type = (insn >> 5) & 3; switch (type) { case 0: val <<= shift; break; case 1: val >>= shift; break; case 2: val = (((signed long)val) >> shift); break; case 3: val = (val >> shift) | (val << (32 - shift)); break; } return val;}#define OP_MASK 0x01e00000#define OP_AND 0x00000000#define OP_EOR 0x00200000#define OP_SUB 0x00400000#define OP_RSB 0x00600000#define OP_ADD 0x00800000#define OP_ADC 0x00a00000#define OP_SBC 0x00c00000#define OP_RSC 0x00e00000#define OP_ORR 0x01800000#define OP_MOV 0x01a00000#define OP_BIC 0x01c00000#define OP_MVN 0x01e00000static unsigned longget_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn){ u32 alt = 0; switch (insn & 0x0e000000) { case 0x00000000: case 0x02000000: { /* * data processing */ long aluop1, aluop2, ccbit; if ((insn & 0xf000) != 0xf000) break; aluop1 = ptrace_getrn(child, insn); aluop2 = ptrace_getaluop2(child, insn); ccbit = get_user_reg(child, REG_PSR) & PSR_C_BIT ? 1 : 0; switch (insn & OP_MASK) { case OP_AND: alt = aluop1 & aluop2; break; case OP_EOR: alt = aluop1 ^ aluop2; break; case OP_SUB: alt = aluop1 - aluop2; break; case OP_RSB: alt = aluop2 - aluop1; break; case OP_ADD: alt = aluop1 + aluop2; break; case OP_ADC: alt = aluop1 + aluop2 + ccbit; break; case OP_SBC: alt = aluop1 - aluop2 + ccbit; break; case OP_RSC: alt = aluop2 - aluop1 + ccbit; break; case OP_ORR: alt = aluop1 | aluop2; break; case OP_MOV: alt = aluop2; break; case OP_BIC: alt = aluop1 & ~aluop2; break; case OP_MVN: alt = ~aluop2; break; } break; } case 0x04000000: case 0x06000000: /* * ldr */ if ((insn & 0x0010f000) == 0x0010f000) { unsigned long base; base = ptrace_getrn(child, insn); if (insn & 1 << 24) { long aluop2; if (insn & 0x02000000) aluop2 = ptrace_getldrop2(child, insn); else aluop2 = insn & 0xfff; if (insn & 1 << 23) base += aluop2; else base -= aluop2; } if (read_u32(child, base, &alt) == 0) alt = pc_pointer(alt); } break; case 0x08000000: /* * ldm */ if ((insn & 0x00108000) == 0x00108000) { unsigned long base; unsigned int nr_regs; if (insn & (1 << 23)) { nr_regs = hweight16(insn & 65535) << 2; if (!(insn & (1 << 24))) nr_regs -= 4; } else { if (insn & (1 << 24)) nr_regs = -4; else nr_regs = 0; } base = ptrace_getrn(child, insn); if (read_u32(child, base + nr_regs, &alt) == 0) alt = pc_pointer(alt); break; } break; case 0x0a000000: { /* * bl or b */ signed long displ; /* It's a branch/branch link: instead of trying to * figure out whether the branch will be taken or not, * we'll put a breakpoint at both locations. This is * simpler, more reliable, and probably not a whole lot * slower than the alternative approach of emulating the * branch. */ displ = (insn & 0x00ffffff) << 8; displ = (displ >> 6) + 8; if (displ != 0 && displ != 4) alt = pc + displ; } break; } return alt;}static intswap_insn(struct task_struct *task, unsigned long addr, void *old_insn, void *new_insn, int size){ int ret; ret = access_process_vm(task, addr, old_insn, size, 0); if (ret == size) ret = access_process_vm(task, addr, new_insn, size, 1); return ret;}static voidadd_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr){ int nr = dbg->nsaved; if (nr < 2) { u32 new_insn = BREAKINST_ARM; int res; res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4); if (res == 4) { dbg->bp[nr].address = addr; dbg->nsaved += 1; } } else printk(KERN_ERR "ptrace: too many breakpoints\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -