一个程序要vm的代码里有几句push pop指令,那vmp虚拟机在遇到这种指令要怎么处理。毕竟vmp自己也是基于堆栈的虚拟机。它会把这些与堆栈操作有关的指令是在真实的堆栈上处理,还是在vmp的虚拟堆栈里处理?
这种需要和宿主打交道的vm,其操作最终会反应到真实机器的寄存器和内存(也就是你这里说的真实的栈,即操作esp和其指向的内容)。
举个例子,伪代码
typedef struct vcpu {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
// ....
uint32_t esp;
} vcpu;
void foo() {
printf("entry_vm\n");
entry_vm();
do_vmcode(vpush, value, 0);
do_vmcode(vadd, eax, ebx);
exit_vm();
// 注意 vpush之后这里栈没平衡
printf("exit_vm\n");
}
这里的 entry_vm 和 exit_vm 一般采用汇编构造,在 entry_vm 阶段保存当前寄存器信息到vcpu结构,exit_vm() 反之。
do_vmcode 会对 vcpu 结构进行操作。
do_vmcode(vpush, value) 操作对应 vcpu->esp-=4; *(int*)(vcpu->esp) = value;
do_vmcode(vadd, eax, ebx) 操作对应 vcpu->eax += vcpu->ebx;
通过以上方式就可以简单将原始汇编二进制转为等价的 vmcode (通常是更为简洁的自定义指令集),这里为了简单用了一一对应的指令关系。
一般流程是 entry_vm 保存寄存器信息后,会进入一个环境初始化部分(负责异常处理等),然后开始一个循环,读取 vcpu->eip 指向的 vmcode,然后调用 do_vmcode 完成上述解析执行操作,直到退出vm,调用 exit_vm(反应寄存器信息回真实机器),在 do_vmcode 中操作的内存即是真实内存,故内存操作在vm内部已经被反应(也可以像 qemu 那样搞个 vmmu,可以,但没必要)。
实际上这种vm最难的地方在于重定位,异常等处理上面,希望有天你能分析分析,写篇报告。