为什么“可执行文件”与操作系统相关?

45
我知道每个CPU/架构都有自己的指令集,因此为特定CPU编写的程序(二进制文件)不能在其他CPU上运行。但我不太理解为什么可执行文件(例如 .exe 这样的二进制文件)不能在 Linux 上运行,却可以在 Windows 上运行,即使在同一台机器上。
这是一个基础问题,我期望的答案是 .exe 和其他二进制格式可能不是原始的机器指令,而是包含一些操作系统相关的数据。如果是这样的话,那么这些与操作系统相关的数据是什么样的?作为例子,.exe 文件的格式是什么,它和 Linux 可执行文件的区别是什么?
是否有一份简洁详细的信息来源可以获取?

4
谁说不能?在Wine的帮助下,Windows二进制文件可以运行在Linux和Mac上。此外,您可能需要在简要和详细需求之间做出妥协。 - David Heffernan
实际上,通常可以通过兼容层(例如WINE)来实现。问题至少有两个方面:文件格式和操作系统接口。后者往往是更大的问题。例如,来自BSD系统的ELF文件将无法在Linux系统上运行,尽管通过映射系统调用的兼容层,反过来却可以运行(取决于某些因素)。 - 0xC0000022L
首先,非常感谢您的回答。我的问题是为什么它们不起作用(没有任何解决方法),但是既然您提到了WINE并且知道了答案,我必须问一个显而易见的问题:如果我们可以使用WINE在Linux上运行.exe文件,那么应该可以实现将.exe文件转换为Linux可执行文件(反之亦然)。这可以通过指令/系统调用/文件结构的简单直接映射来完成。那么我们有这样的转换程序吗? - Mohamed_Ezz
Linux 特定版本:https://dev59.com/fnNA5IYBdhLWcg3wQ7i4 - Ciro Santilli OurBigBook.com
告诉别人用WINE可以在Linux上运行Windows可执行文件,就像告诉别人可以用一个完全模拟Commodore硬件在Windows内部运行的仿真器来在Windows上运行Commodore指令一样。 - upperdim
6个回答

24
为了做一些有意义的事情,应用程序需要与操作系统进行接口。因为Windows和Unix/Linux的系统调用和用户空间基础设施看起来根本不同,拥有不同的可执行程序格式只是最小的问题。需要改变的是程序逻辑
(你可能会争辩说,如果您拥有完全依赖标准化组件(例如C运行时库)的程序,则这是无意义的。理论上是这样的,但对于大多数应用程序来说却是无关紧要的,因为它们被迫使用特定于操作系统的内容)。
Windows PE(EXE、DLL等)文件和Linux ELF二进制文件之间的其他区别与两个操作系统的不同图像加载器和一些设计特征有关。例如,在Linux上,使用单独的程序来解析外部库导入,而在Windows上则内置了该功能。另一个例子是,Linux共享库与Windows上的DLL的功能不同。更不用说两种格式都经过优化,以使相应的操作系统内核尽可能快地加载程序。
类似Wine的仿真器试图填补这一差距(并且实际证明最大的问题不是二进制格式,而是操作系统接口!)。

14
但是 Wine 不是一个模拟器! - mike3996
所以程序逻辑在不同平台上基本上是不同的?内核不仅仅管理线程并将指令传递给CPU吗?这是否与ASCII文本文件略有不同的格式有关?也许这是操作系统相关库的一个例子?有太多问题了,抱歉。 - GPSmaster

5

.exe和其他二进制格式绝对不是原始的机器指令,但它们包含一些操作系统相关的数据。

这种依赖于操作系统的数据是什么样的?以.exe文件为例,请说明其格式及与Linux可执行文件的区别。

好吧,我猜谷歌彻底地让你失望了。.EXE格式在Windows文档中有很明确的定义。

http://support.microsoft.com/kb/65122

Linux的ld应用程序在将可执行文件加载到内存之前执行该文件。您可以阅读关于ld格式或甚至是著名的a.out文件的信息。

http://linux.die.net/man/1/ld

http://en.wikipedia.org/wiki/A.out

http://en.wikipedia.org/wiki/Executable


5
除了必须被系统加载器识别的可执行格式(即操作系统将可执行文件载入内存的部分)之外,真正的问题在于与操作系统的接口。你可以将操作系统看作是一种API,它提供入口点,必须调用这些入口点才能完成特定的任务,例如向控制台写入字符。
这些细节通常对最终用户而言更多或更少地隐藏,因此您可以使用高级语言中相同的源代码实现向屏幕写入字符。但是,有时情况会更加不同,例如窗口环境。并非所有高级语言都提供窗口层,可以将其抽象化以覆盖这些差异。

3

一个非常朴素的答案:

  1. 它们的结构因为不同的进程加载器而不同;
  2. 它们使用与操作系统相关的功能,比如系统调用,这些在不同的操作系统中有所不同。

3
我对*nix不能发表太多评论,但是二进制文件的代码部分通常可以在任何环境下运行,但是操作系统会对二进制文件提出一定的要求。在Windows中,您应该阅读PE Headers
第二部分完全取决于开发人员,很多时候代码部分会引用特定于操作系统的库——这就是为什么在编译成二进制文件之前可以有便携式和非便携式C++代码的原因。

1
程序需要知道如何调用操作系统服务。这取决于操作系统:有些使用中断,有些使用x86的lcall指令,一些(尤其是Windows)具有区分共享库并且不记录如何直接调用服务。旧的680x0 Mac和其他一些680x0操作系统使用了保留的指令集区域,并捕获了由此产生的“无效CPU操作码”异常。此外,即使机制相同,系统调用的顺序和参数格式也因操作系统而异(有时是同一操作系统的不同版本;例如,在Linux内核中的stat()接口已经多次更改)。

有一些处理其他操作系统约定的能力:FreeBSD 有“linuxulator”,它处理 Linux 特定的内核接口,NetBSD 同样具有模拟器,用于处理其他操作系统使用相同硬件的系统调用格式(例如,在 MIPS 上的 Ultrix 或在 Alpha 上的 OSF/1),Linux 曾经有 iBCS2 来处理 UnixWare/SCO Unix 内核接口,Wine 提供了替换共享库和 PE 格式 Windows 可执行文件的二进制加载器。 (我不记得 Wine 是否还支持 OS/2 样式的 LX .exe;它可能确实处理原始格式的 .exe;然后还有一个带有标头的原始内存转储的 .com。)即使如此,总会有一些使用不同约定的格式,有时这些约定足够相似,需要向操作系统提供提示以便处理它。(例如,在 FreeBSD 上看到 bless。)


操作系统服务,例如用户界面?@geekosaur - babygame0ver

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