📄 ptrace.c
字号:
/* * linux/arch/arm/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/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 <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/system.h>#include "ptrace.h"#define REG_PC 15#define REG_PSR 16/* * 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/* fill this in later */#define BREAKINST_THUMB 0xdf00/* * 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. */static inline struct pt_regs *get_user_regs(struct task_struct *task){ return (struct pt_regs *) ((unsigned long)task + 8192 - 8 - sizeof(struct pt_regs));}/* * 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_stack_long(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_stack_long(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;}#ifdef CONFIG_ARM_THUMBstatic inline intread_tsk16(struct task_struct *child, unsigned long addr, unsigned short *res){ int copied; copied = access_process_vm(child, addr, res, sizeof(*res), 0); return copied != sizeof(*res) ? -EIO : 0;}static inline intwrite_tsk16(struct task_struct *child, unsigned long addr, unsigned short val){ int copied; copied = access_process_vm(child, addr, &val, sizeof(val), 1); return copied != sizeof(val) ? -EIO : 0;}static intadd_breakpoint_thumb(struct task_struct *child, struct debug_info *dbg, u32 addr){ int nr = dbg->nsaved; int res = -EINVAL; if (nr < 2) { u16 insn; res = read_tsk16(child, addr, &insn); if (res == 0) res = write_tsk16(child, addr, BREAKINST_THUMB); if (res == 0) { dbg->bp[nr].address = addr; dbg->bp[nr].insn = insn; dbg->nsaved += 1; } } else printk(KERN_ERR "ptrace: too many breakpoints\n"); return res;}#endifstatic inline intread_tsk_long(struct task_struct *child, unsigned long addr, unsigned long *res){ int copied; copied = access_process_vm(child, addr, res, sizeof(*res), 0); return copied != sizeof(*res) ? -EIO : 0;}static inline intwrite_tsk_long(struct task_struct *child, unsigned long addr, unsigned long val){ int copied; copied = access_process_vm(child, addr, &val, sizeof(val), 1); return copied != sizeof(val) ? -EIO : 0;}/* * 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_stack_long(child, reg); if (reg == 15) val = pc_pointer(val + 8); 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_stack_long (child, insn & 15); if (insn & (1 << 4)) shift = (int)get_stack_long (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_stack_long(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){ unsigned long 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_stack_long(child, REG_PSR) & CC_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_tsk_long(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 = insn & 65535; nr_regs = (nr_regs & 0x5555) + ((nr_regs & 0xaaaa) >> 1); nr_regs = (nr_regs & 0x3333) + ((nr_regs & 0xcccc) >> 2); nr_regs = (nr_regs & 0x0707) + ((nr_regs & 0x7070) >> 4); nr_regs = (nr_regs & 0x000f) + ((nr_regs & 0x0f00) >> 8); nr_regs <<= 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_tsk_long(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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -