static int kaif_brkpt_disarm(uintptr_t addr, mdb_instr_t instrp) { if (mdb_tgt_vwrite(mdb.m_target, &instrp, sizeof (mdb_instr_t), addr) != sizeof (mdb_instr_t)) return (-1); /* errno is set for us */ return (0); }
ssize_t mdb_writevar(const void *buf, const char *name) { GElf_Sym sym; if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC, name, &sym, NULL)) return (-1); if (mdb_tgt_vwrite(mdb.m_target, buf, sym.st_size, (uintptr_t)sym.st_value) == sym.st_size) return ((ssize_t)sym.st_size); return (-1); }
static int kaif_brkpt_arm(uintptr_t addr, mdb_instr_t *instrp) { mdb_instr_t bkpt = KAIF_BREAKPOINT_INSTR; if (kaif_toxic_text(addr)) { warn("%a cannot be a breakpoint target\n", addr); return (set_errno(EMDB_TGTNOTSUP)); } if (mdb_tgt_vread(mdb.m_target, instrp, sizeof (mdb_instr_t), addr) != sizeof (mdb_instr_t)) return (-1); /* errno is set for us */ if (mdb_tgt_vwrite(mdb.m_target, &bkpt, sizeof (mdb_instr_t), addr) != sizeof (mdb_instr_t)) return (-1); /* errno is set for us */ return (0); }
ssize_t mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr) { return (mdb_tgt_vwrite(mdb.m_target, buf, nbytes, addr)); }
static int kaif_step(void) { kreg_t pc, fl, oldfl, newfl, sp; mdb_tgt_addr_t npc; mdb_instr_t instr; int emulated = 0, rchk = 0; size_t pcoff = 0; (void) kmdb_dpi_get_register("pc", &pc); if (kaif_toxic_text(pc)) { warn("%a cannot be stepped\n", pc); return (set_errno(EMDB_TGTNOTSUP)); } if ((npc = mdb_dis_nextins(mdb.m_disasm, mdb.m_target, MDB_TGT_AS_VIRT, pc)) == pc) { warn("failed to decode instruction at %a for step\n", pc); return (set_errno(EINVAL)); } /* * Stepping behavior depends on the type of instruction. It does not * depend on the presence of a REX prefix, as the action we take for a * given instruction doesn't currently vary for 32-bit instructions * versus their 64-bit counterparts. */ do { if (mdb_tgt_vread(mdb.m_target, &instr, sizeof (mdb_instr_t), pc + pcoff) != sizeof (mdb_instr_t)) { warn("failed to read at %p for step", (void *)(pc + pcoff)); return (-1); } } while (pcoff++, (instr >= M_REX_LO && instr <= M_REX_HI && !rchk++)); switch (instr) { case M_IRET: warn("iret cannot be stepped\n"); return (set_errno(EMDB_TGTNOTSUP)); case M_INT3: case M_INTX: case M_INTO: warn("int cannot be stepped\n"); return (set_errno(EMDB_TGTNOTSUP)); case M_ESC: if (mdb_tgt_vread(mdb.m_target, &instr, sizeof (mdb_instr_t), pc + pcoff) != sizeof (mdb_instr_t)) { warn("failed to read at %p for step", (void *)(pc + pcoff)); return (-1); } switch (instr) { case M_SYSRET: warn("sysret cannot be stepped\n"); return (set_errno(EMDB_TGTNOTSUP)); case M_SYSEXIT: warn("sysexit cannot be stepped\n"); return (set_errno(EMDB_TGTNOTSUP)); } break; /* * Some instructions need to be emulated. We need to prevent direct * manipulations of EFLAGS, so we'll emulate cli, sti. pushfl and * popfl also receive special handling, as they manipulate both EFLAGS * and %esp. */ case M_CLI: (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); fl &= ~KREG_EFLAGS_IF_MASK; (void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl); emulated = 1; break; case M_STI: (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); fl |= (1 << KREG_EFLAGS_IF_SHIFT); (void) kmdb_dpi_set_register(FLAGS_REG_NAME, fl); emulated = 1; break; case M_POPF: /* * popfl will restore a pushed EFLAGS from the stack, and could * in so doing cause IF to be turned on, if only for a brief * period. To avoid this, we'll secretly replace the stack's * EFLAGS with our decaffeinated brand. We'll then manually * load our EFLAGS copy with the real verion after the step. */ (void) kmdb_dpi_get_register("sp", &sp); (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); if (mdb_tgt_vread(mdb.m_target, &newfl, sizeof (kreg_t), sp) != sizeof (kreg_t)) { warn("failed to read " FLAGS_REG_NAME " at %p for popfl step\n", (void *)sp); return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */ } fl = (fl & ~KREG_EFLAGS_IF_MASK) | KREG_EFLAGS_TF_MASK; if (mdb_tgt_vwrite(mdb.m_target, &fl, sizeof (kreg_t), sp) != sizeof (kreg_t)) { warn("failed to update " FLAGS_REG_NAME " at %p for popfl step\n", (void *)sp); return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */ } break; } if (emulated) { (void) kmdb_dpi_set_register("pc", npc); return (0); } /* Do the step with IF off, and TF (step) on */ (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &oldfl); (void) kmdb_dpi_set_register(FLAGS_REG_NAME, ((oldfl | (1 << KREG_EFLAGS_TF_SHIFT)) & ~KREG_EFLAGS_IF_MASK)); kmdb_dpi_resume_master(); /* ... there and back again ... */ /* EFLAGS has now changed, and may require tuning */ switch (instr) { case M_POPF: /* * Use the EFLAGS we grabbed before the pop - see the pre-step * M_POPFL comment. */ (void) kmdb_dpi_set_register(FLAGS_REG_NAME, newfl); return (0); case M_PUSHF: /* * We pushed our modified EFLAGS (with IF and TF turned off) * onto the stack. Replace the pushed version with our * unmodified one. */ (void) kmdb_dpi_get_register("sp", &sp); if (mdb_tgt_vwrite(mdb.m_target, &oldfl, sizeof (kreg_t), sp) != sizeof (kreg_t)) { warn("failed to update pushed " FLAGS_REG_NAME " at %p after pushfl step\n", (void *)sp); return (set_errno(EMDB_TGTNOTSUP)); /* XXX ? */ } /* Go back to using the EFLAGS we were using before the step */ (void) kmdb_dpi_set_register(FLAGS_REG_NAME, oldfl); return (0); default: /* * The stepped instruction may have altered EFLAGS. We only * really care about the value of IF, and we know the stepped * instruction didn't alter it, so we can simply copy the * pre-step value. We'll also need to turn TF back off. */ (void) kmdb_dpi_get_register(FLAGS_REG_NAME, &fl); (void) kmdb_dpi_set_register(FLAGS_REG_NAME, ((fl & ~(KREG_EFLAGS_TF_MASK|KREG_EFLAGS_IF_MASK)) | (oldfl & KREG_EFLAGS_IF_MASK))); return (0); } }