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명령어와 똑같이 동작해야 하며 스택에서 반환 주소를 검색해야 합니다. (실제 코드에서는 RETs JMP뒤에 a 대신 수동으로 설정한 스택이 따라오거나 CALL, a가 a로 이어지지 않거나, 코드가 CALL스택의 값을 변경하여 RETa의 반환 주소를 변경하는 경우가 많습니다 . RET) 이것이 바로 a입니다. QEMU RET의 역할: gen_pop_T0반환 주소를 스택()에서 꺼내어 처리합니다.

관련 정보