📄 fasttrap_isa.c
字号:
* This is an optimization to let us handle function * return probes more efficiently. Most non-leaf functions * end with the sequence: * ret * restore <reg>, <reg_or_imm>, %oX * * We've stashed the instruction: * restore %g0, %g0, %g0 * * off of %g7 so we just need to place the correct value * in the right %i register (since after our fake-o * restore, the %i's will become the %o's) and set the %pc * to point to our hidden restore. We also set fake_restore to * let fasttrap_return_common() know that it will find the * return values in the %i's rather than the %o's. */ if (I(tp->ftt_instr)) { int32_t imm; imm = tp->ftt_instr << 19; imm >>= 19; value = fasttrap_getreg(rp, RS1(tp->ftt_instr)) + imm; } else { value = fasttrap_getreg(rp, RS1(tp->ftt_instr)) + fasttrap_getreg(rp, RS2(tp->ftt_instr)); } /* * Convert %o's to %i's; leave %g's as they are. */ rd = RD(tp->ftt_instr); fasttrap_putreg(rp, ((rd & 0x18) == 0x8) ? rd + 16 : rd, value); pc = rp->r_g7 + FASTTRAP_OFF_RESTORE; fake_restore = 1; break; } case FASTTRAP_T_RETURN: { uintptr_t target; /* * A return instruction is like a jmpl (without the link * part) that executes an implicit restore. We've stashed * the instruction: * return %o0 * * off of %g7 so we just need to place the target in %o0 * and set the %pc to point to the stashed return instruction. * We use %o0 since that register disappears after the return * executes, erasing any evidence of this tampering. */ if (I(tp->ftt_instr)) { int32_t imm; imm = tp->ftt_instr << 19; imm >>= 19; target = fasttrap_getreg(rp, RS1(tp->ftt_instr)) + imm; } else { target = fasttrap_getreg(rp, RS1(tp->ftt_instr)) + fasttrap_getreg(rp, RS2(tp->ftt_instr)); } fasttrap_putreg(rp, R_O0, target); pc = rp->r_g7 + FASTTRAP_OFF_RETURN; fake_restore = 1; break; } case FASTTRAP_T_OR: { ulong_t value; if (I(tp->ftt_instr)) { int32_t imm; imm = tp->ftt_instr << 19; imm >>= 19; value = fasttrap_getreg(rp, RS1(tp->ftt_instr)) | imm; } else { value = fasttrap_getreg(rp, RS1(tp->ftt_instr)) | fasttrap_getreg(rp, RS2(tp->ftt_instr)); } fasttrap_putreg(rp, RD(tp->ftt_instr), value); pc = rp->r_npc; npc = pc + 4; break; } case FASTTRAP_T_SETHI: if (RD(tp->ftt_instr) != R_G0) { uint32_t imm32 = tp->ftt_instr << 10; fasttrap_putreg(rp, RD(tp->ftt_instr), (ulong_t)imm32); } pc = rp->r_npc; npc = pc + 4; break; case FASTTRAP_T_CCR: { uint_t c, v, z, n, taken; uint_t ccr = rp->r_tstate >> TSTATE_CCR_SHIFT; if (tp->ftt_cc != 0) ccr >>= 4; c = (ccr >> 0) & 1; v = (ccr >> 1) & 1; z = (ccr >> 2) & 1; n = (ccr >> 3) & 1; switch (tp->ftt_code) { case 0x0: /* BN */ taken = 0; break; case 0x1: /* BE */ taken = z; break; case 0x2: /* BLE */ taken = z | (n ^ v); break; case 0x3: /* BL */ taken = n ^ v; break; case 0x4: /* BLEU */ taken = c | z; break; case 0x5: /* BCS (BLU) */ taken = c; break; case 0x6: /* BNEG */ taken = n; break; case 0x7: /* BVS */ taken = v; break; case 0x8: /* BA */ /* * We handle the BA case differently since the annul * bit means something slightly different. */ panic("fasttrap: mishandled a branch"); taken = 1; break; case 0x9: /* BNE */ taken = ~z; break; case 0xa: /* BG */ taken = ~(z | (n ^ v)); break; case 0xb: /* BGE */ taken = ~(n ^ v); break; case 0xc: /* BGU */ taken = ~(c | z); break; case 0xd: /* BCC (BGEU) */ taken = ~c; break; case 0xe: /* BPOS */ taken = ~n; break; case 0xf: /* BVC */ taken = ~v; break; } if (taken & 1) { pc = rp->r_npc; npc = tp->ftt_dest; } else if (tp->ftt_flags & FASTTRAP_F_ANNUL) { /* * Untaken annulled branches don't execute the * instruction in the delay slot. */ pc = rp->r_npc + 4; npc = pc + 4; } else { pc = rp->r_npc; npc = pc + 4; } break; } case FASTTRAP_T_FCC: { uint_t fcc; uint_t taken; uint64_t fsr; dtrace_getfsr(&fsr); if (tp->ftt_cc == 0) { fcc = (fsr >> 10) & 0x3; } else { uint_t shift; ASSERT(tp->ftt_cc <= 3); shift = 30 + tp->ftt_cc * 2; fcc = (fsr >> shift) & 0x3; } switch (tp->ftt_code) { case 0x0: /* FBN */ taken = (1 << fcc) & (0|0|0|0); break; case 0x1: /* FBNE */ taken = (1 << fcc) & (8|4|2|0); break; case 0x2: /* FBLG */ taken = (1 << fcc) & (0|4|2|0); break; case 0x3: /* FBUL */ taken = (1 << fcc) & (8|0|2|0); break; case 0x4: /* FBL */ taken = (1 << fcc) & (0|0|2|0); break; case 0x5: /* FBUG */ taken = (1 << fcc) & (8|4|0|0); break; case 0x6: /* FBG */ taken = (1 << fcc) & (0|4|0|0); break; case 0x7: /* FBU */ taken = (1 << fcc) & (8|0|0|0); break; case 0x8: /* FBA */ /* * We handle the FBA case differently since the annul * bit means something slightly different. */ panic("fasttrap: mishandled a branch"); taken = (1 << fcc) & (8|4|2|1); break; case 0x9: /* FBE */ taken = (1 << fcc) & (0|0|0|1); break; case 0xa: /* FBUE */ taken = (1 << fcc) & (8|0|0|1); break; case 0xb: /* FBGE */ taken = (1 << fcc) & (0|4|0|1); break; case 0xc: /* FBUGE */ taken = (1 << fcc) & (8|4|0|1); break; case 0xd: /* FBLE */ taken = (1 << fcc) & (0|0|2|1); break; case 0xe: /* FBULE */ taken = (1 << fcc) & (8|0|2|1); break; case 0xf: /* FBO */ taken = (1 << fcc) & (0|4|2|1); break; } if (taken) { pc = rp->r_npc; npc = tp->ftt_dest; } else if (tp->ftt_flags & FASTTRAP_F_ANNUL) { /* * Untaken annulled branches don't execute the * instruction in the delay slot. */ pc = rp->r_npc + 4; npc = pc + 4; } else { pc = rp->r_npc; npc = pc + 4; } break; } case FASTTRAP_T_REG: { uint64_t value; uint_t taken; uint_t reg = RS1(tp->ftt_instr); /* * An ILP32 process shouldn't be using a branch predicated on * an %i or an %l since it would violate the ABI. It's a * violation of the ABI because we can't ensure deterministic * behavior. We should have identified this case when we * enabled the probe. */ ASSERT(p->p_model == DATAMODEL_LP64 || reg < 16); value = fasttrap_getreg(rp, reg); switch (tp->ftt_code) { case 0x1: /* BRZ */ taken = (value == 0); break; case 0x2: /* BRLEZ */ taken = (value <= 0); break; case 0x3: /* BRLZ */ taken = (value < 0); break; case 0x5: /* BRNZ */ taken = (value != 0); break; case 0x6: /* BRGZ */ taken = (value > 0); break; case 0x7: /* BRGEZ */ taken = (value <= 0); break; default: case 0x0: case 0x4: panic("fasttrap: mishandled a branch"); } if (taken) { pc = rp->r_npc; npc = tp->ftt_dest; } else if (tp->ftt_flags & FASTTRAP_F_ANNUL) { /* * Untaken annulled branches don't execute the * instruction in the delay slot. */ pc = rp->r_npc + 4; npc = pc + 4; } else { pc = rp->r_npc; npc = pc + 4; } break; } case FASTTRAP_T_ALWAYS: /* * BAs, BA,As... */ if (tp->ftt_flags & FASTTRAP_F_ANNUL) { /* * Annulled branch always instructions never execute * the instruction in the delay slot. */ pc = tp->ftt_dest; npc = tp->ftt_dest + 4; } else { pc = rp->r_npc; npc = tp->ftt_dest; } break; case FASTTRAP_T_RDPC: fasttrap_putreg(rp, RD(tp->ftt_instr), rp->r_pc); pc = rp->r_npc; npc = pc + 4; break; case FASTTRAP_T_CALL: /* * It's a call _and_ link remember... */ rp->r_o7 = rp->r_pc; pc = rp->r_npc; npc = tp->ftt_dest; break; case FASTTRAP_T_JMPL: pc = rp->r_npc; if (I(tp->ftt_instr)) { uint_t rs1 = RS1(tp->ftt_instr); int32_t imm; imm = tp->ftt_instr << 19; imm >>= 19; npc = fasttrap_getreg(rp, rs1) + imm; } else { uint_t rs1 = RS1(tp->ftt_instr); uint_t rs2 = RS2(tp->ftt_instr); npc = fasttrap_getreg(rp, rs1) + fasttrap_getreg(rp, rs2); } /* * Do the link part of the jump-and-link instruction. */ fasttrap_putreg(rp, RD(tp->ftt_instr), rp->r_pc); break; case FASTTRAP_T_COMMON: { curthread->t_dtrace_scrpc = rp->r_g7; curthread->t_dtrace_astpc = rp->r_g7 + FASTTRAP_OFF_FTRET; /* * Copy the instruction to a reserved location in the * user-land thread structure, then set the PC to that * location and leave the NPC alone. We take pains to ensure * consistency in the instruction stream (See SPARC * Architecture Manual Version 9, sections 8.4.7, A.20, and * H.1.6; UltraSPARC I/II User's Manual, sections 3.1.1.1, * and 13.6.4) by using the ASI ASI_BLK_COMMIT_S to copy the * instruction into the user's address space without * bypassing the I$. There's no AS_USER version of this ASI * (as exist for other ASIs) so we use the lofault * mechanism to catch faults. */ if (dtrace_blksuword32(rp->r_g7, &tp->ftt_instr, 1) == -1) { /* * If the copyout fails, then the process's state * is not consistent (the effects of the traced * instruction will never be seen). This process * cannot be allowed to continue execution. */ fasttrap_sigtrap(curproc, curthread, pc); return (0); } curthread->t_dtrace_pc = pc; curthread->t_dtrace_npc = npc; curthread->t_dtrace_on = 1; pc = curthread->t_dtrace_scrpc; if (tp->ftt_retids != NULL) { curthread->t_dtrace_step = 1; curthread->t_dtrace_ret = 1; npc = curthread->t_dtrace_astpc; } break; } default: panic("fasttrap: mishandled an instruction"); } /* * This bit me in the ass a couple of times, so lets toss this * in as a cursory sanity check. */ ASSERT(pc != rp->r_g7 + 4); ASSERT(pc != rp->r_g7 + 8); /* * If there were no return probes when we first found the tracepoint, * we should feel no obligation to honor any return probes that were * subsequently enabled -- they'll just have to wait until the next * time around. */ if (tp->ftt_retids != NULL) { /* * We need to wait until the results of the instruction are * apparent before invoking any return probes. If this * instruction was emulated we can just call * fasttrap_return_common(); if it needs to be executed, we * need to wait until we return to the kernel. */ if (tp->ftt_type != FASTTRAP_T_COMMON) { fasttrap_return_common(rp, orig_pc, pid, fake_restore); } else { ASSERT(curthread->t_dtrace_ret != 0); ASSERT(curthread->t_dtrace_pc == orig_pc); ASSERT(curthread->t_dtrace_scrpc == rp->r_g7); ASSERT(npc == curthread->t_dtrace_astpc); } } ASSERT(pc != 0); rp->r_pc = pc; rp->r_npc = npc; return (0);}intfasttrap_return_probe(struct regs *rp){ proc_t *p = ttoproc(curthread); pid_t pid; uintptr_t pc = curthread->t_dtrace_pc; uintptr_t npc = curthread->t_dtrace_npc; curthread->t_dtrace_pc = 0; curthread->t_dtrace_npc = 0; curthread->t_dtrace_scrpc = 0; curthread->t_dtrace_astpc = 0; /* * Treat a child created by a call to vfork(2) as if it were its * parent. We know there's only one thread of control in such a * process: this one. */ while (p->p_flag & SVFORK) { p = p->p_parent; } /* * We set the %pc and %npc to their values when the traced * instruction was initially executed so that it appears to * dtrace_probe() that we're on the original instruction, and so that * the user can't easily detect our complex web of lies. * dtrace_return_probe() (our caller) will correctly set %pc and %npc * after we return. */ rp->r_pc = pc; rp->r_npc = npc; pid = p->p_pid; fasttrap_return_common(rp, pc, pid, 0); return (0);}intfasttrap_tracepoint_install(proc_t *p, fasttrap_tracepoint_t *tp){ fasttrap_instr_t instr = FASTTRAP_INSTR; if (fasttrap_uwrite(p, &instr, 4, tp->ftt_pc) != 0) return (-1); return (0);}intfasttrap_tracepoint_remove(proc_t *p, fasttrap_tracepoint_t *tp){ fasttrap_instr_t instr; /* * Distinguish between read or write failures and a changed * instruction. */ if (fasttrap_uread(p, &instr, 4, tp->ftt_pc) != 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -