有关 QEMU (x86_64) 中 CALL/RET 指令实现的详细信息

有关 QEMU (x86_64) 中 CALL/RET 指令实现的详细信息

在文件中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指令的方式运行,从堆栈中检索其返回地址。 (在实际代码中,很多情况下RETs 后面JMP带有手动设置的堆栈而不是 a CALL,或者 aCALL永远不会导致 a ,或者代码通过更改堆栈上的值RET来更改 a 的返回地址。 RET) 这就是 QEMU 的RET实现所做的:它从堆栈 ( gen_pop_T0) 中弹出返回地址并对其进行处理。

相关内容