这是一个悬而未决的问题,不确定你想去哪里。维基百科用一个通用的答案涵盖了通用主题。被模拟或虚拟化的本机代码被本机代码替换。代码运行得越多,替换的越多。
我认为您需要做一些事情,首先确定您是在谈论仿真还是像 vmware 或 virtualbox 这样的虚拟机。使用软件对处理器和硬件进行仿真,因此下一条指令由仿真器读取,操作码被代码分开,您决定如何处理它。我一直在做一些 6502 仿真和静态二进制翻译,这是动态重新编译但预先处理而不是实时处理。因此,您的模拟器可能会采用 LDA #10,立即加载 a,模拟器会看到加载 A 立即指令,知道它必须读取下一个字节,即模拟器在 A 寄存器的代码中有一个变量并放入该变量中的立即值。在完成指令之前,模拟器需要更新标志,在这种情况下,零标志清零 N 标志清零 C 和 V 保持不变。但是,如果下一条指令是立即加载 X 怎么办?没什么大不了的吧?好吧,加载 x 也会修改 z 和 n 标志,所以下次执行加载指令时,您可能会发现您不必计算标志,因为它们将被破坏,这是仿真中的死代码。您可以继续这种想法,假设您看到将 x 寄存器复制到 a 寄存器然后将 a 寄存器压入堆栈然后将 y 寄存器复制到 a 寄存器并压入堆栈的代码,您可以替换该块只需将 x 和 y 寄存器压入堆栈即可。或者您可能会看到几个加法与进位链接在一起以执行 16 位加法并将结果存储在相邻的内存位置。基本上是在寻找被模拟的处理器不能做但在模拟中很容易做的操作。我建议您在动态重新编译之前查看静态二进制翻译,以静态方式执行此分析和翻译,就像在您运行代码之前一样。例如,您无需模拟将操作码转换为 C 并尽可能多地删除死代码(一个不错的功能是 C 编译器可以为您删除更多死代码)。
一旦理解了仿真和翻译的概念,那么您可以尝试动态地进行,这当然不是微不足道的。我建议再次尝试将二进制静态转换为目标处理器的机器代码,这是一个很好的练习。我不会尝试动态运行时优化,直到我成功地针对一个/二进制文件静态地执行它们。
虚拟化是一个不同的故事,你说的是在同一个处理器上运行同一个处理器。因此,例如 x86 上的 x86。这里的美妙之处在于,使用非旧 x86 处理器,您可以将正在虚拟化的程序并在实际处理器上运行实际操作码,无需仿真。您设置处理器内置的陷阱来捕获事物,因此在 AX 中加载值并添加 BX 等这些都在处理器上实时发生,当 AX 想要读取或写入内存时,如果地址在内部,则取决于您的陷阱机制虚拟机内存空间,没有陷阱,但可以说程序写入一个地址,即虚拟化 uart,你有处理器陷阱,然后 vmware 或任何解码写入并模拟它与真正的串行端口通信。那条指令虽然不是实时的,但需要很长时间才能执行。如果您选择替换该指令或一组指令,这些指令或指令集将值写入虚拟化串行端口,然后可能已写入可能是真实串行端口或其他不会出现的位置的不同地址,您可以做的导致错误导致 vm 管理器必须模拟该指令。或者在虚拟内存空间中添加一些代码,在没有陷阱的情况下执行对 uart 的写入,然后让该代码分支到这个 uart 写入例程。下次你点击那段代码时,它现在会实时运行。如果您选择替换该指令或一组指令,这些指令或指令集将值写入虚拟化串行端口,然后可能已写入可能是真实串行端口或其他不会出现的位置的不同地址,您可以做的导致错误导致 vm 管理器必须模拟该指令。或者在虚拟内存空间中添加一些代码,在没有陷阱的情况下执行对 uart 的写入,然后让该代码分支到这个 uart 写入例程。下次你点击那段代码时,它现在会实时运行。如果您选择替换该指令或一组指令,这些指令或指令集将值写入虚拟化串行端口,然后可能已写入可能是真实串行端口或其他不会出现的位置的不同地址,您可以做的导致错误导致 vm 管理器必须模拟该指令。或者在虚拟内存空间中添加一些代码,在没有陷阱的情况下执行对 uart 的写入,然后让该代码分支到这个 uart 写入例程。下次你点击那段代码时,它现在会实时运行。
您可以做的另一件事是例如模拟,然后翻译为虚拟中间字节码,例如 llvm。从那里你可以从中间机器翻译到本机机器,最终替换大部分程序,如果不是全部的话。您仍然需要处理外围设备和 I/O。