📄 ptrace.c
字号:
* If the thread is not in an attachable state, we'll * ignore it. The net effect is that if ADDR happens * to overlap with the portion of the thread's * register backing store that is currently residing * on the thread's kernel stack, then ptrace() may end * up accessing a stale value. But if the thread * isn't stopped, that's a problem anyhow, so we're * doing as well as we can... */ return 0; thread_regs = ia64_task_regs(thread); thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL); if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end)) return 0; return 1; /* looks like we've got a winner */}/* * GDB apparently wants to be able to read the register-backing store * of any thread when attached to a given process. If we are peeking * or poking an address that happens to reside in the kernel-backing * store of another thread, we need to attach to that thread, because * otherwise we end up accessing stale data. * * task_list_lock must be read-locked before calling this routine! */static struct task_struct *find_thread_for_addr (struct task_struct *child, unsigned long addr){ struct task_struct *p; struct mm_struct *mm; struct list_head *this, *next; int mm_users; if (!(mm = get_task_mm(child))) return child; /* -1 because of our get_task_mm(): */ mm_users = atomic_read(&mm->mm_users) - 1; if (mm_users <= 1) goto out; /* not multi-threaded */ /* * Traverse the current process' children list. Every task that * one attaches to becomes a child. And it is only attached children * of the debugger that are of interest (ptrace_check_attach checks * for this). */ list_for_each_safe(this, next, ¤t->children) { p = list_entry(this, struct task_struct, sibling); if (p->mm != mm) continue; if (thread_matches(p, addr)) { child = p; goto out; } } out: mmput(mm); return child;}/* * Write f32-f127 back to task->thread.fph if it has been modified. */inline voidia64_flush_fph (struct task_struct *task){ struct ia64_psr *psr = ia64_psr(ia64_task_regs(task)); /* * Prevent migrating this task while * we're fiddling with the FPU state */ preempt_disable(); if (ia64_is_local_fpu_owner(task) && psr->mfh) { psr->mfh = 0; task->thread.flags |= IA64_THREAD_FPH_VALID; ia64_save_fpu(&task->thread.fph[0]); } preempt_enable();}/* * Sync the fph state of the task so that it can be manipulated * through thread.fph. If necessary, f32-f127 are written back to * thread.fph or, if the fph state hasn't been used before, thread.fph * is cleared to zeroes. Also, access to f32-f127 is disabled to * ensure that the task picks up the state from thread.fph when it * executes again. */voidia64_sync_fph (struct task_struct *task){ struct ia64_psr *psr = ia64_psr(ia64_task_regs(task)); ia64_flush_fph(task); if (!(task->thread.flags & IA64_THREAD_FPH_VALID)) { task->thread.flags |= IA64_THREAD_FPH_VALID; memset(&task->thread.fph, 0, sizeof(task->thread.fph)); } ia64_drop_fpu(task); psr->dfh = 1;}static intaccess_fr (struct unw_frame_info *info, int regnum, int hi, unsigned long *data, int write_access){ struct ia64_fpreg fpval; int ret; ret = unw_get_fr(info, regnum, &fpval); if (ret < 0) return ret; if (write_access) { fpval.u.bits[hi] = *data; ret = unw_set_fr(info, regnum, fpval); } else *data = fpval.u.bits[hi]; return ret;}/* * Change the machine-state of CHILD such that it will return via the normal * kernel exit-path, rather than the syscall-exit path. */static voidconvert_to_non_syscall (struct task_struct *child, struct pt_regs *pt, unsigned long cfm){ struct unw_frame_info info, prev_info; unsigned long ip, sp, pr; unw_init_from_blocked_task(&info, child); while (1) { prev_info = info; if (unw_unwind(&info) < 0) return; unw_get_sp(&info, &sp); if ((long)((unsigned long)child + IA64_STK_OFFSET - sp) < IA64_PT_REGS_SIZE) { dprintk("ptrace.%s: ran off the top of the kernel " "stack\n", __FUNCTION__); return; } if (unw_get_pr (&prev_info, &pr) < 0) { unw_get_rp(&prev_info, &ip); dprintk("ptrace.%s: failed to read " "predicate register (ip=0x%lx)\n", __FUNCTION__, ip); return; } if (unw_is_intr_frame(&info) && (pr & (1UL << PRED_USER_STACK))) break; } /* * Note: at the time of this call, the target task is blocked * in notify_resume_user() and by clearling PRED_LEAVE_SYSCALL * (aka, "pLvSys") we redirect execution from * .work_pending_syscall_end to .work_processed_kernel. */ unw_get_pr(&prev_info, &pr); pr &= ~((1UL << PRED_SYSCALL) | (1UL << PRED_LEAVE_SYSCALL)); pr |= (1UL << PRED_NON_SYSCALL); unw_set_pr(&prev_info, pr); pt->cr_ifs = (1UL << 63) | cfm; /* * Clear the memory that is NOT written on syscall-entry to * ensure we do not leak kernel-state to user when execution * resumes. */ pt->r2 = 0; pt->r3 = 0; pt->r14 = 0; memset(&pt->r16, 0, 16*8); /* clear r16-r31 */ memset(&pt->f6, 0, 6*16); /* clear f6-f11 */ pt->b7 = 0; pt->ar_ccv = 0; pt->ar_csd = 0; pt->ar_ssd = 0;}static intaccess_nat_bits (struct task_struct *child, struct pt_regs *pt, struct unw_frame_info *info, unsigned long *data, int write_access){ unsigned long regnum, nat_bits, scratch_unat, dummy = 0; char nat = 0; if (write_access) { nat_bits = *data; scratch_unat = ia64_put_scratch_nat_bits(pt, nat_bits); if (unw_set_ar(info, UNW_AR_UNAT, scratch_unat) < 0) { dprintk("ptrace: failed to set ar.unat\n"); return -1; } for (regnum = 4; regnum <= 7; ++regnum) { unw_get_gr(info, regnum, &dummy, &nat); unw_set_gr(info, regnum, dummy, (nat_bits >> regnum) & 1); } } else { if (unw_get_ar(info, UNW_AR_UNAT, &scratch_unat) < 0) { dprintk("ptrace: failed to read ar.unat\n"); return -1; } nat_bits = ia64_get_scratch_nat_bits(pt, scratch_unat); for (regnum = 4; regnum <= 7; ++regnum) { unw_get_gr(info, regnum, &dummy, &nat); nat_bits |= (nat != 0) << regnum; } *data = nat_bits; } return 0;}static intaccess_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access){ unsigned long *ptr, regnum, urbs_end, rnat_addr, cfm; struct switch_stack *sw; struct pt_regs *pt;# define pt_reg_addr(pt, reg) ((void *) \ ((unsigned long) (pt) \ + offsetof(struct pt_regs, reg))) pt = ia64_task_regs(child); sw = (struct switch_stack *) (child->thread.ksp + 16); if ((addr & 0x7) != 0) { dprintk("ptrace: unaligned register address 0x%lx\n", addr); return -1; } if (addr < PT_F127 + 16) { /* accessing fph */ if (write_access) ia64_sync_fph(child); else ia64_flush_fph(child); ptr = (unsigned long *) ((unsigned long) &child->thread.fph + addr); } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) { /* scratch registers untouched by kernel (saved in pt_regs) */ ptr = pt_reg_addr(pt, f10) + (addr - PT_F10); } else if (addr >= PT_F12 && addr < PT_F15 + 16) { /* * Scratch registers untouched by kernel (saved in * switch_stack). */ ptr = (unsigned long *) ((long) sw + (addr - PT_NAT_BITS - 32)); } else if (addr < PT_AR_LC + 8) { /* preserved state: */ struct unw_frame_info info; char nat = 0; int ret; unw_init_from_blocked_task(&info, child); if (unw_unwind_to_user(&info) < 0) return -1; switch (addr) { case PT_NAT_BITS: return access_nat_bits(child, pt, &info, data, write_access); case PT_R4: case PT_R5: case PT_R6: case PT_R7: if (write_access) { /* read NaT bit first: */ unsigned long dummy; ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, &dummy, &nat); if (ret < 0) return ret; } return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, &nat, write_access); case PT_B1: case PT_B2: case PT_B3: case PT_B4: case PT_B5: return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, write_access); case PT_AR_EC: return unw_access_ar(&info, UNW_AR_EC, data, write_access); case PT_AR_LC: return unw_access_ar(&info, UNW_AR_LC, data, write_access); default: if (addr >= PT_F2 && addr < PT_F5 + 16) return access_fr(&info, (addr - PT_F2)/16 + 2, (addr & 8) != 0, data, write_access); else if (addr >= PT_F16 && addr < PT_F31 + 16) return access_fr(&info, (addr - PT_F16)/16 + 16, (addr & 8) != 0, data, write_access); else { dprintk("ptrace: rejecting access to register " "address 0x%lx\n", addr); return -1; } } } else if (addr < PT_F9+16) { /* scratch state */ switch (addr) { case PT_AR_BSP: /* * By convention, we use PT_AR_BSP to refer to * the end of the user-level backing store. * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) * to get the real value of ar.bsp at the time * the kernel was entered. * * Furthermore, when changing the contents of * PT_AR_BSP (or PT_CFM) we MUST copy any * users-level stacked registers that are * stored on the kernel stack back to * user-space because otherwise, we might end * up clobbering kernel stacked registers. * Also, if this happens while the task is * blocked in a system call, which convert the * state such that the non-system-call exit * path is used. This ensures that the proper * state will be picked up when resuming * execution. However, it *also* means that * once we write PT_AR_BSP/PT_CFM, it won't be * possible to modify the syscall arguments of * the pending system call any longer. This * shouldn't be an issue because modifying * PT_AR_BSP/PT_CFM generally implies that * we're either abandoning the pending system * call or that we defer it's re-execution * (e.g., due to GDB doing an inferior * function call). */ urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); if (write_access) { if (*data != urbs_end) { if (ia64_sync_user_rbs(child, sw, pt->ar_bspstore, urbs_end) < 0) return -1; if (in_syscall(pt)) convert_to_non_syscall(child, pt, cfm); /* * Simulate user-level write * of ar.bsp: */ pt->loadrs = 0; pt->ar_bspstore = *data; } } else *data = urbs_end; return 0; case PT_CFM: urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); if (write_access) { if (((cfm ^ *data) & PFM_MASK) != 0) { if (ia64_sync_user_rbs(child, sw, pt->ar_bspstore, urbs_end) < 0) return -1; if (in_syscall(pt)) convert_to_non_syscall(child, pt, cfm); pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) | (*data & PFM_MASK)); } } else *data = cfm; return 0; case PT_CR_IPSR: if (write_access) pt->cr_ipsr = ((*data & IPSR_MASK) | (pt->cr_ipsr & ~IPSR_MASK)); else *data = (pt->cr_ipsr & IPSR_MASK); return 0; case PT_AR_RSC: if (write_access) pt->ar_rsc = *data | (3 << 2); /* force PL3 */ else *data = pt->ar_rsc; return 0; case PT_AR_RNAT: urbs_end = ia64_get_user_rbs_end(child, pt, NULL); rnat_addr = (long) ia64_rse_rnat_addr((long *) urbs_end); if (write_access) return ia64_poke(child, sw, urbs_end, rnat_addr, *data); else return ia64_peek(child, sw, urbs_end, rnat_addr, data); case PT_R1: ptr = pt_reg_addr(pt, r1); break; case PT_R2: case PT_R3: ptr = pt_reg_addr(pt, r2) + (addr - PT_R2); break; case PT_R8: case PT_R9: case PT_R10: case PT_R11: ptr = pt_reg_addr(pt, r8) + (addr - PT_R8); break; case PT_R12: case PT_R13: ptr = pt_reg_addr(pt, r12) + (addr - PT_R12); break; case PT_R14: ptr = pt_reg_addr(pt, r14); break; case PT_R15: ptr = pt_reg_addr(pt, r15); break; case PT_R16: case PT_R17: case PT_R18: case PT_R19: case PT_R20: case PT_R21: case PT_R22: case PT_R23: case PT_R24: case PT_R25: case PT_R26: case PT_R27: case PT_R28: case PT_R29: case PT_R30: case PT_R31: ptr = pt_reg_addr(pt, r16) + (addr - PT_R16); break; case PT_B0: ptr = pt_reg_addr(pt, b0); break; case PT_B6: ptr = pt_reg_addr(pt, b6); break; case PT_B7: ptr = pt_reg_addr(pt, b7); break; case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: ptr = pt_reg_addr(pt, f6) + (addr - PT_F6); break; case PT_AR_BSPSTORE: ptr = pt_reg_addr(pt, ar_bspstore); break; case PT_AR_UNAT: ptr = pt_reg_addr(pt, ar_unat); break; case PT_AR_PFS: ptr = pt_reg_addr(pt, ar_pfs); break; case PT_AR_CCV: ptr = pt_reg_addr(pt, ar_ccv); break; case PT_AR_FPSR: ptr = pt_reg_addr(pt, ar_fpsr); break; case PT_CR_IIP: ptr = pt_reg_addr(pt, cr_iip); break; case PT_PR: ptr = pt_reg_addr(pt, pr); break; /* scratch register */ default: /* disallow accessing anything else... */ dprintk("ptrace: rejecting access to register " "address 0x%lx\n", addr); return -1; } } else if (addr <= PT_AR_SSD) { ptr = pt_reg_addr(pt, ar_csd) + (addr - PT_AR_CSD); } else { /* access debug registers */ if (addr >= PT_IBR) { regnum = (addr - PT_IBR) >> 3; ptr = &child->thread.ibr[0]; } else { regnum = (addr - PT_DBR) >> 3; ptr = &child->thread.dbr[0]; } if (regnum >= 8) { dprintk("ptrace: rejecting access to register " "address 0x%lx\n", addr); return -1; }#ifdef CONFIG_PERFMON /* * Check if debug registers are used by perfmon. This * test must be done once we know that we can do the * operation, i.e. the arguments are all valid, but * before we start modifying the state. * * Perfmon needs to keep a count of how many processes * are trying to modify the debug registers for system * wide monitoring sessions. * * We also include read access here, because they may * cause the PMU-installed debug register state * (dbr[], ibr[]) to be reset. The two arrays are also * used by perfmon, but we do not use * IA64_THREAD_DBG_VALID. The registers are restored * by the PMU context switch code. */ if (pfm_use_debug_registers(child)) return -1;#endif if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { child->thread.flags |= IA64_THREAD_DBG_VALID; memset(child->thread.dbr, 0, sizeof(child->thread.dbr)); memset(child->thread.ibr, 0, sizeof(child->thread.ibr)); } ptr += regnum; if ((regnum & 1) && write_access) { /* don't let the user set kernel-level breakpoints: */ *ptr = *data & ~(7UL << 56); return 0; } } if (write_access) *ptr = *data; else *data = *ptr; return 0;}static longptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr){ unsigned long psr, ec, lc, rnat, bsp, cfm, nat_bits, val; struct unw_frame_info info; struct ia64_fpreg fpval; struct switch_stack *sw; struct pt_regs *pt; long ret, retval = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -