编译器实际上会产生机器代码吗?

时间:2018-10-20 00:02:58

标签: gcc compilation programming-languages cpu machine-code

我一直在阅读,在大多数情况下(例如gcc),编译器以高级语言读取源代码,并吐出相应的机器代码。现在,按照定义,机器代码是处理器可以直接理解的代码。因此,机器代码应仅取决于机器(处理器)且与操作系统无关。但这种情况并非如此。即使两个不同的操作系统在同一处理器上运行,我也无法在两个操作系统上运行相同的编译文件(对于Windows是.exe或对于Linux是.out)。

那么,我想念什么? gcc编译器(和大多数编译器)的输出不是机器代码吗?还是不是机器代码不是最低级别的代码,而是操作系统将其进一步翻译为处理器可以执行的一组指令?

4 个答案:

答案 0 :(得分:1)

编译器产生汇编代码,这是机器代码的人类可读版本(例如,您具有实际命令,而不是1和0)。但是,使程序正确运行所需的正确程序集/机器代码因操作系统而异。因此,处理器使用的语言是相同的,但是您的程序需要与操作系统对话,这是不同的。

例如,假设您正在编写Hello World程序。您需要在屏幕上打印短语“ Hello,World”。您的程序将需要通过操作系统来实际执行此操作,并且不同的操作系统具有不同的界面。

我在这里特意避免使用技术术语,以使初学者可以理解答案。更准确地说,您的程序需要通过操作系统才能与计算机上的其他硬件(例如,键盘,显示器)进行交互。这是通过system calls完成的,每个操作系统系列都不同。

答案 1 :(得分:1)

生成的机器代码可以在为其生成的任何相同类型的处理器上运行。挑战在于,您的代码将与系统上的其他模块或程序进行交互,并且为此,您需要调用和返回的约定。生成的代码假定运行时环境(OS)以及库支持(调用约定)。跨操作系统不一致。

因此,当它们需要使用操作系统的调用约定定义的约定过渡到并依赖于其他模块时,事情就会中断。

答案 2 :(得分:0)

您正在混淆一些事情。我将可重定位目标的编译器(例如gcc)和其他通用编译器将文件编译为对象,然后链接器随后根据需要将对象与其他库链接在一起,以生成所谓的二进制文件,操作系统可以读取,解析,加载可加载的块并开始执行。 / p>

一个精明的编译器作者将使用汇编语言作为编译器的输出,然后编译器或其makefile中的用户将调用创建该对象的汇编器。这就是gcc的工作方式。以及clang的工作方式sorta,但是llc现在可以直接制造对象,而不仅仅是被组装的组件。

生成可生成原始机器代码的可调试汇编语言更为有意义。您确实需要像JIT这样的充分理由才能跳过此步骤。我会避免直接使用机器代码的工具链,因为它们可以,它们更难维护,并且更有可能出现错误,或者花费更长的时间来修复错误。

如果架构相同,则没有理由不能让通用工具链为不兼容的操作系统生成代码。例如,gnu工具可以做到这一点。操作系统差异不是在机器代码级别定义的,大多数是在高级语言级别的C库中,您可以创建gui窗口,等等与机器代码或处理器体系结构无关,对于某些操作系统而言,相同操作系统特定的C代码可在mips或arm或powerpc或x86上使用。特定于体系结构的地方是调用实际系统调用的机制。通常使用特定的说明。并最终使用了机器代码,但是没有理由不能在实际或内联汇编中对此进行编码。

然后导致生成库,即使是通用C调用的fopen和printf最终也必须进行系统调用,因此许多库支持代码可以在跨系统高级语言中兼容,因此需要最后一英里的特定于系统和体系结构的代码。您应该在glibc源代码中看到这一点,或者例如在其他库解决方案中挂接到newlib。作为例子。

对于其他语言(例如C ++)和C语言也是如此。解释性语言具有附加层,但它们的虚拟机只是位于相似层上的程序。

低级编程并不意味着机器语言或汇编语言,它仅表示您正在使用的任何编程语言都可以在较低级别,应用程序下方或操作系统下进行访问,等等。

答案 3 :(得分:0)

即使在两个不同的操作系统上编译程序的机器代码指令相同(完全不可能,因为不同的操作系统以不同的方式提供不同的服务),机器代码也需要以一种主机OS可以使用“加载到”进程来执行。而且这些格式在不同的操作系统之间经常不同。