为什么机器代码取决于操作系统类型?

dnn*_*agy 3 c assembly linker compilation machine-code

假设,我有一个用C编写的程序,我有两台相同的计算机,一台运行Windows,另一台运行Linux.由于计算机是相同的,它们的处理器具有相同的指令集,因此编译后的机器代码应该相同.那么为什么我需要两次编译我的程序呢?假设,我不会调用任何与操作系统相关的功能,或者取决于实际操作系统的功能.

Ped*_*d7g 6

机器代码不依赖于OS,对于相同的CPU也是如此.

如果你做了OS不可知的机器代码,在目标CPU模式下(假设x86 32b),并将其加载到某些ROM内存中,那么它将可用,你可以在Windows和Linux中映射那部分ROM(通过完全不同的OS API来映射物理内存并赋予它可执行权限,并跳转到那里..并且ROM中的机器代码将以相同的方式运行.

那么为什么我需要两次编译我的程序呢?假设,我不会调用任何与操作系统相关的功能,或者取决于实际操作系统的功能.

你不必.但通常你需要一些入口点进入你的代码,通常最简单的方法是如何提供通用入口点是遵循OS定义的ABI(应用程序二进制接口),所以例如在32b窗口中你从堆栈中读取参数,并在64b中linux你在寄存器中接收参数(如果可能的话).如果你不调整你的程序序言代码以正确的方式选择参数,那么它将在"其他"操作系统中以错误的输入操作而不是为其编写.

但机器代码本身,CPU指令是相同的.

也就是说,在x86上由于历史向后兼容性,情况有点毛茸茸,所以CPU可以处于16b模式,32b [protected]模式(其中一对加上不同的设置)或64b模式.80386 CPU指令mov eax,1针对16b模式和32b模式具有不同的机器代码编码.

但只要您的目标是相同的CPU模式,相同指令的机器代码就会以相同的方式编译.您只需按照不同的ABI来编写源代码即可.

而且可执行文件......每种格式都不同,它甚至不是"按操作系统",再次由于历史原因,几乎所有x86操作系统都支持几种可执行文件格式,因此机器代码周围的元数据存储在文件中(待使用)在将机器代码加载到内存并将其设置为运行时,通过操作系统完全不同.

实际的例子是linux app wine,它可以执行Windows可执行文件,通过提供假的OS挂钩点来模拟Windows操作系统,并通过了解Windows可执行二进制文件,所以正确地将它们加载到内存中.这种Windows应用程序的机器代码是本机运行的,无需进一步修补.

  • 再一记。汇编程序通常会生成“目标文件”,这些文件也是特定于工具链的格式,因此 Microsoft Visual Studio 使用不同的“.obj”文件来存储相同的组装机器代码,而不是 linux 上的 gcc 生成“.o”。目标文件的机器码部分相同,但允许链接此类文件的元数据可能完全不同(加上调试元数据的不同格式等)。所以这是另一个原因,为什么您必须多次编译相同的源代码,但它不是每个操作系统,而是每个工具链。 (2认同)
  • 此外,类型大小的ABI差异:`long`在x86-64 Windows上为32位,在System V x86-64 ABI中为64位.因此,在为不同的ABI编译时,相同的C结构可能意味着不同的东西,更不用说单个变量的大小不同(因此在机器代码中需要不同的操作数大小,本地人需要不同的堆栈布局等) (2认同)
  • 它比"工具链"更糟糕.它是每个版本的工具链.对于来自特定供应商的所有工具,永久性不保证中间文件的二进制兼容性.我不确定GCC的人们是否试图在这里提供向后兼容性,但微软明确没有.(实际上这很讽刺.)跨工具链唯一有效的是调试信息,因为它具有标准化格式.但是,当然,关于标准的最好的事情是有很多可供选择:COFF,ELF,CodeView,PDB,... :-) (2认同)