Gcc 编译器是否实际生成机器代码?

Gcc 编译器是否实际生成机器代码?,gcc,compilation,programming-languages,cpu,machine-code,Gcc,Compilation,Programming Languages,Cpu,Machine Code,我一直在读到,在大多数情况下(如gcc),编译器用高级语言读取源代码,并输出相应的机器代码。根据定义,机器代码是处理器可以直接理解的代码。因此,机器代码应该只依赖于机器(处理器)和操作系统。但事实并非如此。即使两个不同的操作系统在同一个处理器上运行,我也不能在两个操作系统上运行相同的编译文件(.exe for Windows或.out for Linux) 那么,我错过了什么?gcc编译器(和大多数编译器)的输出不是机器代码吗?或者机器代码不是最低级别的代码,操作系统将其进一步转换为处理器可以执

我一直在读到,在大多数情况下(如gcc),编译器用高级语言读取源代码,并输出相应的机器代码。根据定义,机器代码是处理器可以直接理解的代码。因此,机器代码应该只依赖于机器(处理器)和操作系统。但事实并非如此。即使两个不同的操作系统在同一个处理器上运行,我也不能在两个操作系统上运行相同的编译文件(.exe for Windows或.out for Linux)


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

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

例如,假设您正在编写一个Hello World程序。您需要在屏幕上打印短语“你好,世界”。你的程序需要通过操作系统才能真正做到这一点,不同的操作系统有不同的接口


我在这里故意避免使用专业术语,以使初学者能够理解答案。更准确地说,您的程序需要通过操作系统与计算机上的其他硬件(如键盘、显示器)进行交互。这是通过不同的操作系统系列来实现的。

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


因此,当它们需要转换到并依赖于使用操作系统调用约定定义的约定的其他模块时,情况就会发生变化。

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

一个健全的编译器作者将使用汇编语言作为编译器的输出,然后编译器或其makefile中的用户调用创建对象的汇编程序。这就是gcc的工作原理。还有clang是如何工作的sorta,但llc现在可以直接制作对象,而不仅仅是组装好的组件

生成生成原始机器代码的可调试汇编语言更有意义。你真的需要一个像JIT这样的好理由来跳过这一步。我会避免直接进入机器代码的工具链,因为它们可以,它们更难维护,更有可能出现错误或需要更长的时间来修复错误

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

然后这就导致了库,甚至是fopen和printf,它们都是通用的C调用,最终必须进行系统调用。因此,库支持代码中的大部分都可以使用跨系统兼容的高级语言,在最后一英里中,将需要一个系统和体系结构特定的代码位。您应该在glibc源代码中看到这一点,或者在其他库解决方案中挂钩到newlib。作为例子

对于C++等其他语言,同样的情况也是如此。解释语言有附加的层,但是它们的虚拟机只是处于相似层上的程序。


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

即使在两个不同的操作系统上编译的程序的机码指令相同(不太可能,因为不同的操作系统以不同的方式提供不同的服务),机器代码需要以主机操作系统可以使用“加载到”进程中执行的格式存储。而这些格式在不同的操作系统之间通常是不同的