如何使编译后的二进制文件在另一个系统上无需重新编译即可完美运行?

3
我知道很多人一看到这个问题就会立刻喊出“Java”,但不,我知道Java的优点。请允许我先解释一下我的问题。
通常情况下,如果我们想要在系统上以本地速度运行程序,无论是Windows、Mac OS X还是Linux,我们需要从源代码进行编译。如果你想在你的系统上运行另一个系统的程序,你需要使用虚拟机或模拟器。虽然这些工具允许您在非本机操作系统上使用所需的程序,但有时会出现性能和故障问题。
我们还有一个名为“JIT编译器”的较新编译器,其中编译器将在执行之前将字节码程序解析为本机机器语言。 JIT编译器的性能可能会大大提高,但性能仍然不同于在本机系统上运行它。
Linux上的另一个程序WINE也是在Linux系统上运行Windows程序的好工具。我曾尝试在其中运行Team Fortress 2,并尝试进行一些设置实验。在Windows上,我在1280 x 1024的中高设置下得到了约40帧每秒的速度。在Linux上,我需要将所有设置调低到1280 x 1024才能获得约40帧每秒的速度。有两个值得注意的事情:
1. 多边形模型设置似乎不会影响帧速率,无论我将它设置为低或高。 2. 当有后处理效果或某些需要操作当前帧绘制像素的特殊效果时,帧速率会降至10-20帧每秒。
从这一点上来看,普通多边形渲染是完全正常的,但当涉及到需要图形卡完成的新渲染方法时,速度就会变慢。
总之,这个问题相当理论化。我们能做些什么吗?我看到WINE可以运行STEAM和Team Fortress 2。虽然有缺陷,但它们可以以较低的设置运行。或者,我也应该问一下,“是否可能在不重新编译源代码的情况下将一个系统上的整个程序翻译到另一个系统并获得本地速度?”我看到我们还有AOT编译器,是否可能将其用于此类问题?或者有许多限制(如DirectX调用或软件架构差异)使得没有缺陷且非本机系统的程序能以本地速度运行成为不可能?

4
读到“本机速度”这个词汇,我真的想不到会是关于“Java”的。请问需要怎样翻译呢? - Cody Gray
2个回答

6
在不重新编译的情况下,使相同的代码在多个系统上以本地速度运行的第一步是选择一个处理器指令集并丢弃所有其他系统。如果您选择Intel,则必须放弃ARM、MIPS、PowerPC等,因为一个架构的本机机器码指令对于其他处理器来说完全无法理解。
好的。现在的任务是在多个系统上(所有系统都使用相同的处理器架构)以本地速度运行相同的编译后的本机代码而不需要重新编译。所以基本上,您想在同一硬件上的不同操作系统下运行相同的代码。
如果硬件相同,唯一的区别是操作系统,则微不足道的答案是肯定的,如果您可以在不调用操作系统的情况下编写代码。没有内存分配。没有控制台输出。没有文件I/O。没有网络I/O。没有乐趣。
此外,您的代码将必须以这样一种方式编写,即该代码不需要地址重定位修复,因为每个操作系统都有表示可重定位代码的不同方式。一种方法是将代码按照与在内存中出现的方式完全排列在磁盘上,包括保留用于可写数据(全局变量、堆栈和堆)的空间。然后,要运行代码,您只需将文件字节复制到预定义的基址处的内存中,然后跳转到起始地址。
至少需要为要在其中运行您的本机代码的每个操作系统编译一个程序加载器。当您为要针对的每个操作系统编写程序加载器时,还可以定义自己的文件I/O函数,这些函数映射到操作系统本机等效项,以便您的本机代码块可以在任何系统上执行文件I/O。控制台I/O或图形输出也是如此。
哦,等等-这正是WINE所做的。
这也是为什么您在WINE中看到的帧速率比主机操作系统中相同的操作要低得多的原因-WINE将Win32 GDI图形调用转换为由本机主机操作系统(Linux-> XWindows)提供的内容,并且在没有精确功能匹配或存在操作语义不匹配的情况下(这经常是情况),WINE必须自己实现所有功能,有时代价很大。
但是鉴于IDE驱动器、USB设备和BIOS功能等标准化硬件的普及,也许您不需要费尽心思将自己的便携式API映射到操作系统中已经内置的内容上。只需编写一小段代码即可对IDE设备进行文件I/O操作,使用VESA BIOS函数进行图形输出。如果您稍微抽象一下代码,就可以支持多种硬件,并根据运行时找到的硬件选择适当的函数指针来使用。
然后,您可以真正以本机速度在任何系统上(使用特定的处理器架构)运行您的本机代码,而无需重新编译。
哦,等等——您刚刚编写了自己的操作系统。 ;>

1
“病毒扫描器会在除主机操作系统以外的任何程序尝试将文件数据加载到内存并执行时变得非常兴奋。”哦,对了,还有一个非常清晰、深思熟虑的解释。 - Cody Gray

4
是的,技术上可以将为一个处理器架构和操作系统编写的二进制可执行程序翻译成另一个处理器和操作系统上可以运行的二进制可执行程序。但这是一项非常艰巨的工作。
“本机代码执行速度”术语存在问题。您可以使用禁用优化的方式将程序编译为本机代码,生成的代码将是以“本机代码执行速度”运行的本机可执行代码,但它可能会比启用优化编译的相同源代码运行得更慢。两者都在运行“本机代码执行速度”,但它们在运行不同数量和质量的机器代码来实现相同的核心算法。
机器指令比高级源编程语言要原始得多。将源代码编译为机器代码时,会丢失很多信息。例如,数据类型通常被编译器缩减为少量的机器基元-指针、整数、浮点数。字符串是指向内存的指针。char是一个整数。对象实例是一个指针。
当您将一组机器指令转换为另一组机器指令时,您会受到限制,因为您没有像源代码编译器那样对数据有足够的了解。从源代码编译时,编译器可以看到数据中的关系和优化,这些关系和优化只通过查看机器代码很难发现。
故事时间:Digital Equipment Corporation创建了一个名为FX!32的系统,它接收本地编译的Win32 Intel x86可执行文件,对其进行反编译,并将逻辑转换为在运行Windows NT AXP的本机Alpha AXP处理器指令。在这种情况下,操作系统至少是从同一布料中剪裁出来的,但其中一个是32位,另一个是64位,在机器代码级别上,它们具有根本不同的调用约定。尽管如此,它仍然可以正常工作,并且效果非常好。由于硬件上的差异,Intel x86应用程序在AXP上运行可能比在Intel硬件上运行的同一应用程序更快。(FX!32在运行几次Intel应用程序后使用分析来重新优化AXP代码,因此性能通常开始非常糟糕,但每次运行应用程序时都会提高)
但是,即使一切都是执行本机AXP指令,FX!32翻译的应用程序也从未像针对AXP指令集重新编译源代码那样快。 FX!32翻译的本机AXP指令流由于需要完全表示原始Intel x86指令的语义,即使(看不见的)更高级别算法不需要所有这些语义的方面,也被肿胀了。
在进行机器指令到机器指令的翻译时,您可以听到交响乐中的每个音符,但您可能难以挑出定义旋律的音符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接