为什么机器码可执行文件依赖于操作系统?

8
在Windows中,当我编译一个简单的"C"程序时,会得到最终的可执行机器码.exe文件。而在Unix系统中,使用gcc编译同样的程序会生成一个.out机器码文件。
这两者有什么区别呢?
我的基本问题是,.exe.out作为机器码,为什么它们与操作系统有关?
也就是说,在Unix系统中我不能直接执行.exe文件,在Windows中我也不能执行Unix的.out文件。为什么会这样?

2
实际上,Unix系统并不像Windows那样严格地使用文件扩展名。在Unix中,您可以随意命名文件。除非您指定其他输出文件名,否则gcc通常会生成名为“a.out”的文件。 - Platinum Azure
3个回答

12

这一切都与程序的加载方式有关。

Windows和Linux对于程序自身定义采用不同的格式。

在Linux中通常使用ELF格式,而在Windows中则是PE

这些格式定义了关于程序所需执行机器指令的不同数据。

此外,操作系统的接口也不同,需要使用不同的库和进行不同的系统调用。

对于简单的程序,通常只需要在另一个操作系统上重新编译即可使其运行在两个操作系统上,但你将无法在两个操作系统上使用一个单独的文件。


1
我本来想写一个完整的答案,但这就是它会添加到你的答案中的全部内容,而你已经先行了。*nix系统:ELF。Windows系统:PE - nmichaels
感谢提供链接,我已将它们添加到答案中以达到完整性。 - Alan Geleynse
1
重要的一点是,可执行文件通常*不仅仅是原始的机器代码 - 它们是将机器代码与许多其他数据打包在一起的东西,告诉操作系统如何加载和运行代码。 - caf
不要在同一个文件上操作?实际上,在Linux上,你可以设置支持自定义二进制格式(使用Google的binfmt_misc),这样你就可以透明地将PE可执行文件传递给Wine :-) - Christoffer

1
操作系统抽象了对底层硬件的访问,并通过系统调用向程序员提供。在Windows中,这些是通过Windows API完成的(通常由使编程更容易的库进一步抽象,如MFC等)。在UNIX中,这通常是通过中断完成的,系统的C库通过遵循POSIX api(通常带有一些系统相关的添加)使其变得更加容易。
例如,在Linux上,系统调用是通过int 0x80进行的,加载了几个寄存器以传递函数的参数,C库通过允许您使用预期的参数( int fd, void *buf, size_t count )来调用read等函数,从而使其更加容易。这将被转换为中断调用,内核将对其做出响应。
这两种针对操作系统发出请求的方式不兼容,因此您(通常)无法在UNIX系统上运行Windows可执行文件,反之亦然,除非使用作为翻译层的其他系统,如WINE、VMWare等(尽管这两种方式的工作方式非常不同)。
(顺便说一下,a.out并不涉及可执行文件的内容;它是在UNIX系统上编译可执行文件时给出的传统文件名,缩写为“汇编器输出”。GCC允许交叉编译,因此您甚至可以使用它来编译Win32兼容的.EXE文件。您可以使用-o标志来指定gcc的输出文件名,这表明它与输出文件的实际格式无关。)

1
Windows API最终确实会使用其中一种陷入内核指令“int”、“syscall”或“sysenter”(如果我没记错的话,从ntdll中)来进行系统调用,就像Unix一样。(在x86上,调用门也是另一个可能性,但据我所知,由于速度较慢,没有人再使用它们。)当然,陷入号、系统调用编号和系统调用语义完全不同。 - zwol
谢谢,我猜最终这样的事情是必要的,但我早就离开了这个平台。 - Sdaz MacSkibbons

0
在Unix环境中,任何设置了+x位的文件都被视为可执行文件。请记住,即使是非二进制文件也可以是可执行文件(如shell脚本、批处理文件等)。Windows依赖于文件扩展名的概念,而在Unix上我们只需设置chmod +x filename即可。
您始终可以使用-o file标志来强制gcc生成任何您喜欢的文件名。

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