static int prepare_emulation()

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;
}