在文件中target/i386/translate.c
指令CALL
(操作码 0xe8)具有以下实现:
case 0xe8: /* call im */
{
if (dflag != MO_16) {
tval = (int32_t)insn_get(env, s, MO_32);
} else {
tval = (int16_t)insn_get(env, s, MO_16);
}
next_eip = s->pc - s->cs_base;
tval += next_eip;
if (dflag == MO_16) {
tval &= 0xffff;
} else if (!CODE64(s)) {
tval &= 0xffffffff;
}
tcg_gen_movi_tl(cpu_T0, next_eip);
gen_push_v(s, cpu_T0);
gen_bnd_jmp(s);
gen_jmp(s, tval);
}
break;
中的值next_eip
由以下调用保存:
tcg_gen_movi_tl(cpu_T0, next_eip);
gen_push_v(s, cpu_T0);
但我找不到这个值(next_eip
)在实现中是如何使用的RET
:
case 0xc3: /* ret */
ot = gen_pop_T0(s);
gen_pop_update(s, ot);
/* Note that gen_pop_T0 uses a zero-extending load. */
gen_op_jmp_v(cpu_T0);
gen_bnd_jmp(s);
gen_jr(s, cpu_T0);
break;
当我跟踪CALL
实现时,我看到使用返回地址的代码:
void tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) { TCGOp *op = tcg_emit_op(opc); // INDEX_op_movi_i64 op->args[0] = a1; // address of register op->args[1] = a2; // **REAL RETURN ADDRESS** }
RET
但我在跟踪实现时找不到真实的返回地址。
谁能告诉我RET
指令实现到底在哪里使用next_eip
.
答案1
中的值next_eip
从堆栈中弹出:
ot = gen_pop_T0(s);
作为副作用,此更新cpu_T0
然后用于跳转;请参阅执行gen_pop_T0
:
TCGMemOp d_ot = mo_pushpop(s, s->dflag);
gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1);
gen_op_ld_v(s, d_ot, cpu_T0, cpu_A0);
return d_ot;
这是RET
检索由 推送的值的方法CALL
:
tcg_gen_movi_tl(cpu_T0, next_eip);
gen_push_v(s, cpu_T0);
在解释RET
指令时,模拟器不能依赖内部知识:它必须完全按照RET
指令的方式运行,从堆栈中检索其返回地址。 (在实际代码中,很多情况下RET
s 后面JMP
带有手动设置的堆栈而不是 a CALL
,或者 aCALL
永远不会导致 a ,或者代码通过更改堆栈上的值RET
来更改 a 的返回地址。 RET
) 这就是 QEMU 的RET
实现所做的:它从堆栈 ( gen_pop_T0
) 中弹出返回地址并对其进行处理。