机器代码可以翻译成不同的架构吗?

机器代码可以翻译成不同的架构吗?

这与关于在 ARM 上运行 Windows 服务器所以我的问题的前提是,机器代码能否从一种架构翻译到另一种架构为了执行二进制文件在与编译时运行的体系结构不同的体系结构上。

QEMU 和其他模拟器可以即时翻译指令,因此可以在未编译的计算机上运行可执行文件。为什么不提前进行翻译,而不是即时进行翻译以加快进程?根据我对汇编的有限了解,大多数指令(如 、MOVADD其他指令)应该可以跨架构移植。

由于所有机器都是图灵完备的,因此任何没有直接映射的东西都可以映射到其他指令集。这样做会不会太复杂?会不会因为某些我不熟悉的原因而根本不起作用?会不会起作用,但不会比使用模拟器产生更好的结果?

答案1

简短的回答:您无法翻译已编译、已链接的可执行文件。虽然从技术上讲可行,但实现的可能性极小(见下文)。 然而,如果你有汇编源文件(包含说明和标签),这是非常有可能的(尽管如果你以某种方式获得汇编源代码,除非程序是用汇编语言编写的,否则你也应该有原始程序源代码,所以你最好一开始就为不同的架构编译它)。


详细答案

QEMU 和其他模拟器可以即时翻译指令,因此可以在未编译的计算机上运行可执行文件。为什么不提前进行翻译,而不是即时进行翻译以加快进程呢?

我知道这在理论上似乎很容易,但在实践中,由于几个主要原因,这几乎是不可能的。首先,不同的指令集使用截然不同的寻址模式、不同的操作码结构、不同的字长,有些甚至没有您需要的指令。

假设您需要用XYZ另外两条指令ABC和来替换指令DEF。现在您已经有效地从该点开始移动了整个程序中的所有相对/偏移地址,因此您需要分析并检查整个程序并更新偏移量(更改之前和之后)。现在,假设其中一个偏移量发生了显着变化 - 现在您需要更改寻址模式,这可能会改变地址的大小。这将再次迫使您重新扫描整个文件并重新计算所有地址,依此类推。

当你编写汇编程序时,你可能会使用标签,但 CPU 不会——当文件被汇编时,所有的标签都被计算为相对、绝对或偏移位置。你可以看到为什么这很快就会成为一项不简单的任务,而且几乎不可能。替换单身的指令可能要求您在继续之前先完成整个程序数百次。

从我对汇编的有限了解来看,大多数指令(例如 MOV、ADD 等)应该可以跨架构移植。

是的,但请看一下我上面列出的问题。机器的字长呢?地址长度呢?它是否具有相同的寻址模式?同样,您不能只是“查找和替换”指令。程序的每个段都有一个明确定义的地址。在汇编程序时,跳转到其他标签将被文字或偏移内存地址替换。

由于所有机器都是图灵完备的,因此任何没有直接映射的东西都可以映射到其他指令集。这样做会不会太复杂?会不会因为某些我不熟悉的原因而根本不起作用?会不会起作用,但不会比使用模拟器产生更好的结果?

你完全正确,两者都是可能的,而且会很多快点。但是,编写一个程序来实现这一点是极其困难和极不可能的,如果不是因为我上面概述的问题之外的其他原因的话。

如果你有实际的汇编源代码,那么将机器代码翻译成另一种指令集架构就很简单了。然而,机器代码本身组装因此,如果没有汇编源代码(其中包含用于计算内存地址的各种标签),这将变得异常困难。同样,更改一条指令可能会改变整个程序中的内存偏移量,并且需要数百次传递才能重新计算地址。

对于包含几千条指令的程序,执行此操作需要数万甚至数十万次传递。对于相对较小的程序,这可能是可能的,但请记住,传递次数将随着程序中的机器指令数量而呈指数增加。对于任何足够大的程序,这几乎是不可能的。

答案2

是的,您建议的方法可以实现,而且已经有人实现了。这种方法并不常见,我也不知道目前有哪些系统使用这种技术,但它绝对在技术可行性范围内。

在实现我们现在所拥有的甚至粗糙的“可移植性”之前,人们曾做过很多工作来将代码从一个系统移植到另一个系统。这需要对“源代码”进行复杂的分析,并且可能会因代码修改和其他奇怪的做法而受阻,但人们还是这样做了。

最近,诸如 IBM System/38 -- iSeries -- System i 之类的系统利用了与编译程序一起存储的中间代码(类似于 Java 字节码)的可移植性,实现了不兼容的指令集架构之间的可移植性。

答案3

您所描述的过程称为静态重新编译,它已经实现,只是方式不普遍。这意味着它不可能实现,它已经实现过很多次,但需要手动操作。

历史上有许多值得研究的例子,但它们不太能说明现代人所关心的问题。我发现了两个例子,它们应该会让任何完全怀疑论者质疑那些声称一切困难都是不可能的事情的人。

首先,这个家伙为 NES ROM 做了一个完整的静态架构和平台。 http://andrewkelley.me/post/jamulator.html

他提出了一些非常好的观点,但最后得出结论,JIT 仍然更实用。我实际上不确定他为什么不知道,对于这种情况,这可能是大多数人考虑的情况类型。不走捷径,要求全周期准确性,并且基本上不使用 ABI。如果这就是全部,我们可以把这个概念扔进垃圾桶,然后就此打住,但这不是全部,也从来不是全部……我们怎么知道的?因为所有成功的项目都没有使用这种方法。

现在来看看不太明显的可能性,利用您已有的平台……在 Linux ARM 手持设备上玩星际争霸?是的,当您不将任务限制为您动态执行的任务时,这种方法是有效的。通过使用 Winlib,Windows 平台调用都是本机的,我们只需担心架构。

http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/

考虑到 ARM 手持式 Pandora 仅比 Pi 强一点,我敢打赌,减速几乎可以忽略不计。他使用的工具位于此存储库中。

https://github.com/notaz/ia32rtools

那家伙反编译得非常手动,我相信这个过程可以大大自动化,工作量更少……但目前仍然是一项艰巨的工作。不要让任何人告诉你某事是不可能的,甚至不要让我告诉你它不切实际……它可能会变得实用,只要你创新出一种新方法来实现它。

答案4

理论上是可以的。更大的问题是将一个操作系统(或内核)的应用程序转换为另一个操作系统(或内核)。Windows、Linux、OSX 和 iOS 内核的低级操作之间存在显著差异,这些设备的所有应用程序都必须使用这些操作。

再次,从理论上讲,人们可以编写一个应用程序,它可以分解应用程序以及与编译运行该应用程序的操作系统相关的所有机器代码,然后为另一台设备重新编译所有机器代码。然而,这在几乎所有情况下都是非常违法的,而且编写起来非常困难。事实上,光是想想这件事,我的脑子就开始发麻了。

更新

下面有几条评论似乎不同意我的回复,但我认为他们没有理解我的观点。据我所知,没有一个应用程序能够获取一个架构的可执行字节序列,在字节码级别对其进行分解,包括对外部库的所有必要调用,包括对底层操作系统内核的调用,然后将其重新组装到另一个系统,然后保存生成的可执行字节码换句话说,没有任何应用程序可以将像 Notepad.exe 这样简单的程序分解为 190k 的小文件,然后 100% 重新组装为可以在 Linux 或 OSX 上运行的应用程序。

据我了解,提问者想知道,如果我们可以虚拟化软件或通过 Wine 或 Parallels 等程序运行应用程序,为什么我们不能简单地为不同的系统重新翻译字节码。原因是,如果您想要为另一种架构完全重新组装应用程序,则必须先分解运行它所需的所有字节码,然后再重新组装它。每个应用程序都不仅仅是 exe 文件,例如对于 Windows 计算机来说。所有 Windows 应用程序都使用低级 Windows 内核对象和函数来创建菜单、文本区域、窗口调整大小的方法、绘制到显示器、发送/接收操作系统消息等等……

如果您想要重新组装应用程序并使其在不同的架构上运行,则必须反汇编所有的字节码。

Wine 等应用程序在字节级别解释 Windows 二进制文件。它们识别对内核的调用,并将这些调用转换为相关的 Linux 函数或模拟 Windows 环境。但这不是逐字节(或逐操作码)的重新翻译。它更像是逐函数的翻译,这有很大不同。

相关内容