为什么 Linux 原生不能运行 exe 格式的文件?

exe里面存放的不是汇编指令吗?如果是汇编指令的话 x86下Windows编译的EXE 为什么不能在 x86下的Linux上运行?还是说Windows…
关注者
660
被浏览
491,415

99 个回答

首先文件格式不同,Windows下的EXE是PE格式(64位版本叫PE32+),而Linux下的可执行程序是ELF32或ELF64格式的。在Linux下面运行EXE,系统会尝试以ELF格式解析PE文件,显然不会成功。这类的可执行文件开头往往有一个magic number,ELF文件是“\0x7fELF”,PE文件则是“MZ”。

除此之外,系统调用的接口也不同。32位的Linux使用0x80软中断,64位的Linux采用汇编指令syscall,同时还定义了一套专门的调用约定(calling convention)。Windows没有公开NT内核的系统调用规范,而是用WIN32 API进一步封装,用户程序通过系统的DLL访问系统功能。在不同版本的NT内核之间,系统调用接口可能都是不一致的,更不要说NT和Linux之间的区别了。(虽然NT系统调用规范不公开,但是能够分析出来,详见 github.com/j00ru/window

除了文件格式以及系统调用,还有一个关键区别就是共享库。Linux下称作shared object,Windows下简称DLL。一个可执行文件可能需要很多共享库才能运行,例如Windows下的msvcrt.dll。共享库是操作系统提供的,动态链接也是操作系统负责,不同系统的行为各不相同。

以上是不同操作系统下不支持其他OS可执行文件的几个原因。其实,如果把上面三个原因分别解决,也可以实现Windows下运行Linux程序,以及Linux下运行EXE。

在Linux下运行EXE,最有名的解决方案是wine(Wine Is Not an Emulator)。既然Windows不公开系统调用,完全通过win32 API访问内核功能,那么只要使用Linux的系统调用提供一套新的win32 API实现,就能让EXE跑在Linux中了,毕竟接口都定义好了。wine另一个任务就是解析PE格式的文件,既然Linux不认识PE,那么就要添加一个内核模块,让内核能够识别并加载EXE,同时提供一套替代的系统DLL。

Windows下运行Linux二进制程序,代表是Windows 10内置的WSL(Windows Subsystemfor Linux)。关于WSL的实现机制我了解不多,听说是将所有的Linux系统调用使用NT的系统调用实现了一遍,并没有使用Linux源代码。感觉WSL要比wine相对容易一些,因为Linux只是一个内核,标准库GLibC、动态链接器ld.so并不属于内核。WSL只需要提供一个ELF格式的解析器,并提供一套Linux API接口的实现,剩下用户态的东西,标准库、shell之类的,全部直接拿来。因此,WSL能够允许多个发行版同时共存。

自古以来,Linux都不能运行windows平台的exe二进制可执行文件,windows也不能运行Linux的二进制ELF文件。

但是,大人们,现在不一样啦:

湾区美女Justine Tunney搞了个大新闻: 现在,我们终于能够实现exe在Linux和Windows上都跑起来的宏伟目标啦!

这里,不只是这个,该大神把C语言搞成了能够本地运行于Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS七大系统的神器了。

她做了个跨平台的C标准库 Cosmopolitan Libc ,能够把C语言程序变成为“一次编译到处运行的语言”(build-once run-anywhere language)。没错,就是Java当年叫嚣的口号。

不过,这里的C可执行文件可不需要解释器或者虚拟机,而是真正的本地运行的二进制文件,POSIX可运行的多语言格式,能够本地运行于Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS,7大平台。

一个100多k的小文件就可以完美的运行于以上7大平台了,甚至其中包括了BIOS。惊不惊喜,意不意外。

这可不是Java或者C#的虚拟机平台,又或者是Docker之流能比的了。

不信看这里:

为了描述这种C语言的二进制跨平台文件格式,她甚至给它取了个名字:APE,即αcτµαlly pδrταblε εxεcµταblε。官网的字体就是那样写的,但我读起来感觉就是:Actually portable executable。这个名字完美的契合了“build-once run-anywhere”的口号。

经过以上骚操作,就把标准C语言变成了一个完美的build-once run-anywhere的语言了。

当然,这年头,杠精尤其多,他们会说,这有什么的,又不能运行于ARM上。

看这里,下面这货就能让C的二进制程序运行于ARM,比如树莓派:

那么说了那么多,这种东西性能如何呢?

提前泄露以下吧。这本身就是原生的二进制文件,性能肯定是遥遥领先于其他Java、Go语言了。

结论说完了,不愿看测评的小伙伴可以撤了。


1)测试1

首先,来测试经典的fibnacci数:

#include"cosmopolitan.h"
int fib(int n)
 if (n <= 2)
 return 1;
 return fib(n-1) + fib(n-2);
int main(int argc, char **argv) 
 int n;
 if (argc < 2) {
 printf("usage: fib n\n"
  "Compute nth Fibonacci number\n");
 return 1;
 n = atoi(argv[1]);
 clock_t st, end;
 st = clock();
 int f=fib(n);
 end = clock();