in kernel/kprobes/core.c [574:687]
static int prepare_emulation(struct kprobe *p, struct insn *insn)
{
insn_byte_t opcode = insn->opcode.bytes[0];
switch (opcode) {
case 0xfa: /* cli */
case 0xfb: /* sti */
case 0x9c: /* pushfl */
case 0x9d: /* popf/popfd */
/*
* IF modifiers must be emulated since it will enable interrupt while
* int3 single stepping.
*/
p->ainsn.emulate_op = kprobe_emulate_ifmodifiers;
p->ainsn.opcode = opcode;
break;
case 0xc2: /* ret/lret */
case 0xc3:
case 0xca:
case 0xcb:
p->ainsn.emulate_op = kprobe_emulate_ret;
break;
case 0x9a: /* far call absolute -- segment is not supported */
case 0xea: /* far jmp absolute -- segment is not supported */
case 0xcc: /* int3 */
case 0xcf: /* iret -- in-kernel IRET is not supported */
return -EOPNOTSUPP;
break;
case 0xe8: /* near call relative */
p->ainsn.emulate_op = kprobe_emulate_call;
if (insn->immediate.nbytes == 2)
p->ainsn.rel32 = *(s16 *)&insn->immediate.value;
else
p->ainsn.rel32 = *(s32 *)&insn->immediate.value;
break;
case 0xeb: /* short jump relative */
case 0xe9: /* near jump relative */
p->ainsn.emulate_op = kprobe_emulate_jmp;
if (insn->immediate.nbytes == 1)
p->ainsn.rel32 = *(s8 *)&insn->immediate.value;
else if (insn->immediate.nbytes == 2)
p->ainsn.rel32 = *(s16 *)&insn->immediate.value;
else
p->ainsn.rel32 = *(s32 *)&insn->immediate.value;
break;
case 0x70 ... 0x7f:
/* 1 byte conditional jump */
p->ainsn.emulate_op = kprobe_emulate_jcc;
p->ainsn.jcc.type = opcode & 0xf;
p->ainsn.rel32 = *(char *)insn->immediate.bytes;
break;
case 0x0f:
opcode = insn->opcode.bytes[1];
if ((opcode & 0xf0) == 0x80) {
/* 2 bytes Conditional Jump */
p->ainsn.emulate_op = kprobe_emulate_jcc;
p->ainsn.jcc.type = opcode & 0xf;
if (insn->immediate.nbytes == 2)
p->ainsn.rel32 = *(s16 *)&insn->immediate.value;
else
p->ainsn.rel32 = *(s32 *)&insn->immediate.value;
} else if (opcode == 0x01 &&
X86_MODRM_REG(insn->modrm.bytes[0]) == 0 &&
X86_MODRM_MOD(insn->modrm.bytes[0]) == 3) {
/* VM extensions - not supported */
return -EOPNOTSUPP;
}
break;
case 0xe0: /* Loop NZ */
case 0xe1: /* Loop */
case 0xe2: /* Loop */
case 0xe3: /* J*CXZ */
p->ainsn.emulate_op = kprobe_emulate_loop;
p->ainsn.loop.type = opcode & 0x3;
p->ainsn.loop.asize = insn->addr_bytes * 8;
p->ainsn.rel32 = *(s8 *)&insn->immediate.value;
break;
case 0xff:
/*
* Since the 0xff is an extended group opcode, the instruction
* is determined by the MOD/RM byte.
*/
opcode = insn->modrm.bytes[0];
if ((opcode & 0x30) == 0x10) {
if ((opcode & 0x8) == 0x8)
return -EOPNOTSUPP; /* far call */
/* call absolute, indirect */
p->ainsn.emulate_op = kprobe_emulate_call_indirect;
} else if ((opcode & 0x30) == 0x20) {
if ((opcode & 0x8) == 0x8)
return -EOPNOTSUPP; /* far jmp */
/* jmp near absolute indirect */
p->ainsn.emulate_op = kprobe_emulate_jmp_indirect;
} else
break;
if (insn->addr_bytes != sizeof(unsigned long))
return -EOPNOTSUPP; /* Don't support different size */
if (X86_MODRM_MOD(opcode) != 3)
return -EOPNOTSUPP; /* TODO: support memory addressing */
p->ainsn.indirect.reg = X86_MODRM_RM(opcode);
#ifdef CONFIG_X86_64
if (X86_REX_B(insn->rex_prefix.value))
p->ainsn.indirect.reg += 8;
#endif
break;
default:
break;
}
p->ainsn.size = insn->length;
return 0;
}