QEMU(x86_64)のCALL/RET命令の実装に関する詳細情報

QEMU(x86_64)のCALL/RET命令の実装に関する詳細情報

ファイル内のtarget/i386/translate.cコマンドCALL(opcode 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命令と同じように動作し、スタックから戻りアドレスを取得する必要があります。 (実際のコードでは、RETsJMPの後にaCALLの代わりに手動で設定したスタックが付いてくる、aがaにつながらない、またはコードがスタックのCALL値を変更してRETaの戻りアドレスを変更することが多いです。RET)これがまさにaです。 QEMURETの役割:gen_pop_T0戻りアドレスをスタック()から取り出し、処理します。

おすすめ記事