#ifdef CONFIG_X86_64
unsigned long saved_scratch_register;
#endif
+#define UPROBE_CLEAR_TF (1 << 0)
+ unsigned int restore_flags;
};
extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
/* Adjust the return address of a call insn */
#define UPROBE_FIX_CALL 0x2
+/* Instruction will modify TF, don't change it */
+#define UPROBE_FIX_SETF 0x4
+
#define UPROBE_FIX_RIP_AX 0x8000
#define UPROBE_FIX_RIP_CX 0x4000
insn_get_opcode(insn); /* should be a nop */
switch (OPCODE1(insn)) {
+ case 0x9d:
+ /* popf */
+ auprobe->fixups |= UPROBE_FIX_SETF;
+ break;
case 0xc3: /* ret/lret */
case 0xcb:
case 0xc2:
}
return false;
}
+
+void arch_uprobe_enable_step(struct arch_uprobe *auprobe)
+{
+ struct uprobe_task *utask = current->utask;
+ struct arch_uprobe_task *autask = &utask->autask;
+
+ autask->restore_flags = 0;
+ if (!test_tsk_thread_flag(current, TIF_SINGLESTEP) &&
+ !(auprobe->fixups & UPROBE_FIX_SETF))
+ autask->restore_flags |= UPROBE_CLEAR_TF;
+ /*
+ * The state of TIF_BLOCKSTEP is not saved. With the TF flag set we
+ * would to examine the opcode and the flags to make it right. Without
+ * TF block stepping makes no sense.
+ */
+ user_enable_single_step(current);
+}
+
+void arch_uprobe_disable_step(struct arch_uprobe *auprobe)
+{
+ struct uprobe_task *utask = current->utask;
+ struct arch_uprobe_task *autask = &utask->autask;
+
+ if (autask->restore_flags & UPROBE_CLEAR_TF)
+ user_disable_single_step(current);
+}