O valor em next_eip
é retirado da pilha:
ot = gen_pop_T0(s);
Como efeito colateral, isso atualiza cpu_T0
, que é então usado para o salto; veja a implementação de 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;
É assim que RET
recupera o valor que foi enviado por CALL
:
tcg_gen_movi_tl(cpu_T0, next_eip);
gen_push_v(s, cpu_T0);
Ao interpretar uma instrução RET
, o emulador não pode confiar no conhecimento interno: ele deve se comportar exatamente como a instrução RET
, recuperando seu endereço de retorno da pilha. (No código real, há muitos casos de RET
s seguindo um JMP
com uma pilha configurada manualmente em vez de um CALL
ou um CALL
nunca resultando em RET
ou código alterando o endereço de retorno para o RET
alterando o valor na pilha.) Isso é o que a implementação RET
do QEMU faz: ele aparece o endereço de retorno da pilha ( gen_pop_T0
) e processa isso.