不同的编译器是否会为 Windows 生成不同的机器代码文件格式?

不同的编译器是否会为 Windows 生成不同的机器代码文件格式?

在 Windows 下,如果有人使用 gcc 编译 C++ 源代码,输出文件格式和扩展名是否与其他 C++ 编译器不同?

C++ 编译器的输出文件扩展名和格式与 C# 编译器的输出是否不同?或者您可以选择任何您想要的输出文件?

答案1

其中涉及几种不同类型的文件。

(传统) 编译器首先生成一个“目标文件”,其中包含与输入到编译器的 .c (etc) 文件相对应的指令。此文件是“可重定位”和“可链接”的,这意味着它不假定它被加载到特定地址,并且它仅包含对其调用的其他代码的“符号”(即名称字符串) 引用。

“目标文件”通过“链接器”(或“链接加载器”或“链接编辑器”)与其他目标文件相结合,生成一个“可执行”文件,该文件已“解析”单独目标文件之间的所有引用,并且至少完成了部分“重定位”(地址分配)。

然后,可执行文件由“加载器”处理,将文件复制到可执行 RAM 中并准备运行。“加载器”将执行任何必要的最终“重定位”,定位可执行文件的入口点等。

还可能有“库文件”,它是“目标文件”的一种字典,可用于“解析”来自其他目标文件的引用。还可能有“模块”或类似的东西(名称因平台而异),它是对象模块的预链接子集。

这些文件中的每一个都可能具有几种不同的格式。例如,原始 DOS/Windows 中存在 COM 文件格式和 EXE 文件格式,它们都是“可执行”文件,随时可以加载。我听说过有多种“目标文件”格式的系统,但我记不清了。制作一个链接器并不难,比如检查它的传入“目标文件”并根据发现的类型对它们进行不同的处理。(但我不知道是否有任何当前的 Windows 工具可以做到这一点。)

请注意,C# 和 Java(以及几种历史语言,如 UCSD Pascal)是不同的,因为它们生成的“字节码”文件旨在被解释而不是链接、加载和执行。

答案2

文件格式是指文件的组织方式。Windows 要求二进制可执行文件具有某种格式。无论它们是用什么语言编译的,都无关紧要。

一般来说,任何给定语言的同一源代码都有多个机器代码。因此,不同的编译器可能会从同一源代码创建不同的二进制文件。但它们会做完全相同的事情。事实上,如果您更改代码优化选项或其他参数,一个给定的编译器可能会从同一源代码生成不同的二进制文件。

对于等效源代码,C# 编译器肯定会创建与 C++ 编译器不同的二进制文件。

答案3

如果有人用 gcc 编译器编译了 c++ 源代码,输出文件格式是否与其他 c++ 编译器的输出不同?

可能吧。有很多方法可以将 C++ 代码翻译成机器语言,尤其是在 x86/amd64 上,它有各种各样的指令,可以以多种方式组合,除非编译器本身共享相同的代码,否则它们可能不会以完全相同的方式执行此操作。一些简单的东西可能会重合,比如switch语句等等。

如果“文件格式”指的是可执行格式(例如 Windows 可移植可执行格式),则任何编译器必须这样做才能生成你可以运行的东西。

并且 c++ 编译器的输出文件格式与 c# 编译器的输出不同吗?或者您可以选择任何您想要的输出文件?

实质上,因为 C# 是 .NET 的一部分,并且编译为 CIL,而不是本机代码。我不太确定 Visual C++ 是否允许您在编译为本机或 CIL 之间进行选择。我相信 C# 只允许您编译为 CIL。我可能错了。

答案4

哎呀,问题很复杂……

第一部分,关于C++能否匹敌C++

所有这些都假设相同的芯片/abi。如果我们没有这种匹配,我们将根本无法兼容。我们需要 32 位 x86 代码匹配 32 位 x86 代码,或者 64 位 AMD64 代码匹配 AMD64 代码。

1) 对于 C(我们从简单的开始,稍后讨论 C++)对象,不同编译器的输出与其他编译器兼容。有一种东西叫做芯片澳大利亚商业信息局,这样可以保持一致。我们匹配设置堆栈的方式、设置寄存器的方式等。很早以前,由于符号长度而存在一些不兼容性,但这个问题已经解决了多年。

这是可行的,因为 C 相对简单。我们需要同步什么?函数名称和函数调用。这些都在 ABI 中

2) 对于 C++,我们有很多旋钮需要调整。我们如何布局一个对象?我们如何/何时调用静态构造函数/析构函数?我们如何布局一个对象以进行继承?还是多重继承?虚拟函数?即使是“简单”的事情,比如我们如何“修改”函数名称(我们如何重命名函数的符号,使符号名称中具有参数类型)。我们在这里要做很多决定,我们有很多不同的路径可以下去。

一开始,C++ 编译器供应商只做他们认为最好的事情。可以理解为:他们想做什么就做什么,根本不关心兼容性,所以没有兼容性。后来他们意识到这使得库的销售变得不可能——你需要为每个编译器版本(或至少是很大一部分版本)编译一个 C++ 库。

最终他们想出了标准 C++ ABI。这允许来自多个编译器的代码组合在一起。这是一个更严格的布局和名称列表,以确保代码可以正确地相互调用。

耶!所有现代编译器都符合标准!一切都完成了!对吧?

不是真的……首先,如果你要迁移到新的 ABI,这意味着你与旧的 ABI 不兼容。其次,你正在按照规范进行编码,但不能保证你做得正确。在 G++ 试图与规范匹配时,他们犯了几次错误。

因此,总结一下 C++:

有一个规范应该能够使 C++ 代码在各个编译器之间兼容。但这取决于编译器是否按照规范进行编码,以及它们是否正确理解了规范。对于现代编译器(过去 5 年内的任何编译器),这应该是正确的。

现在,关于 C++ 和 C#....

C++ 旨在在芯片上运行。直接在 X86 或 AMD64 上运行,无论什么。因此,它具有针对这些特定芯片的操作码。芯片读取数字,了解它代表什么命令,并执行它要求的操作。

C# 旨在在虚拟机(称为 CLI)上运行。因此,它有不同的命令,并且数字也不相同。这就像让一个只会说英语的人阅读法语的汽车调校说明。这根本行不通。

相关内容