为什么仿真必须实时完成?

时间:2012-06-26 20:48:49

标签: architecture binary emulation cpu

我理解模拟器的作用,将一种机器语言改为另一种,通常是“即时”。这样的程序是否可以构建为读取为一个体系结构编写的二进制文件并为另一个体系结构保存新的二进制文件。在该过程完成之后,它将使用户准备好二进制文件以在给定体系结构上进行本机执行。对于那些拥有昂贵的传统架构专有应用程序的人来说,这将特别有用。

是否可以提出这样的申请?二进制重新编译不是一个新概念,但我还没有找到任何有用的实现。

在其他一些人的帮助下,我很高兴能开始对这样一个程序的开源实现进行编码,如果编程可能的话。

3 个答案:

答案 0 :(得分:6)

静态重新编译是一种将二进制文件从外部体系结构转换为另一种目标体系结构的有前途的方法。它比Just-In-Time(JIT)更快,因为它不必在运行之前编译代码,并且因为它可能需要额外的编译时间来优化生成代码。

然而,JIT编译使用动态程序分析,而静态重新编译依赖于静态程序分析(因此名称)。

在静态分析中,您没有关于执行的运行时信息。

这是一个主要问题是间接跳跃。该术语涵盖了可能从某些switch语句,使用函数指针或运行时多态(生成虚拟表)生成的代码。 这一切都归结为以下形式的指示:

JMP reg_A 

假设您知道程序的起始地址,并且您决定从这一点开始重新编译指令。遇到直接跳转时,转到目标地址,然后从那里继续重新编译。当你遇到间接跳跃时,你会陷入困境。 在此汇编指令中,reg_A的内容不是静态知道的。 因此,我们不知道下一条指令的地址。请注意,在动态重新编译中,我们没有这个问题,因为我们模拟寄存器的虚拟状态,并且我们知道reg_A的当前内容。此外,在静态重新编译中,您有兴趣在此时为reg_A找到所有可能的值,因为您希望编译所有可能的路径。在动态分析中,您只需要当前值来生成当前正在执行的路径,如果reg_A更改其值,您仍然可以生成其他路径。 在某些情况下,静态分析可以找到一个候选列表(如果是switch必须有一个可能的偏移表,但在一般情况下我们根本就不知道。

很好,你说,让我们重新编译二进制文件中的所有指令吧!

这里的问题是大多数二进制文件都包含代码和数据。 根据架构,您可能无法分辨哪个是。

更糟糕的是,在某些体系结构中没有对齐约束和可变宽度指令,您可能会在某些时候开始反汇编,只是发现您已经开始使用偏移重新编译。

我们采用一个包含两条指令和一个寄存器A的简化指令集:

41 xx (size 2): Add xx to `A`.
42 (size 1): Increment `A` by one.

我们采取以下二进制程序:

41 42

假设起点是第一个字节41。 你这样做:

 41 42 (size 2): Add 42 to `A`.

但如果41是一段数据怎么办?然后你的程序变成:

 42 (size 1): Increment `A` by one.

这个问题在旧游戏中被放大,旧游戏通常直接在汇编中进行优化,而程序员可能会故意expect some byte to be interpreted as both code and data, depending on the context!

更糟糕,重新编译的程序本身就可以生成代码!想象一下重新编译JIT编译器。结果仍然会输出源架构的代码并尝试跳转到它,很可能导致程序很快死掉。静态重新编译仅在运行时可用的代码需要无限的诡计!

静态二进制分析是一个非常活跃的研究领域(主要是在安全领域,寻找源不可用的系统中的漏洞),实际上我知道尝试生成NES emulator that tries to statically recompile programs。 这篇文章非常有趣。

JIT和静态重新编译之间的折衷方案是静态地重新编译尽可能多的代码,只保留不能静态转换的位。

答案 1 :(得分:3)

我相信您正在寻找静态与动态重新编译。动态重新编译就是您所描述的“实时”仿真或重新编译。代码以块的形式重新编译,允许仿真器准确反映原始代码的运行时环境。

如果有可能,您会询问静态重新编译。有些人已经指出,在许多不同的情况下都是可能的,但是在静态重新编译之后,期望非常特定的运行时约束的代码可能无法成功运行。这就是为什么使用静态重新编译的N64仿真器Corn只能运行极少数高度优化的游戏,而采用动态重新编译的其他N64仿真器运行的游戏种类更多。

对于更复杂和传统的代码(即x86到PowerPC),静态重新编译确实是可能的,但是这样的事情将证明非常繁琐,因为重新编译器必须使用大量技巧来使生成的静态代码可靠地运行在目标机器上。动态重新编译器可以在运行时动态执行此操作,只需花费一小部分开发工作量,并且性能成本可以忽略不计。

答案 2 :(得分:0)

您必须首先确保使用此方法重新编译任何引用的库。

这是可能的,但却是一项艰巨的任务。

还要考虑这样做会导致许可问题;您正在基于原始软件创建衍生作品。允许您执行此操作的大多数许可证也将允许您拥有源代码,因此您可以重新编译或移植源代码,这样更容易。