源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件
对于gcc而言:
第一步 预处理
命令: gcc -o test.i -E test.c
或者 cpp -o test.i test.c (这里cpp不是值c plus plus,而是the C Preprocessor)
结果: 生成预处理后的文件test.i(可以打开后与预处理前进行比对,当然长度会吓你一跳)
作用: 读取c源程序,对伪指令和特殊符号进行处理。包括宏,条件编译,包含的头文件,以及一些特殊符号。基本上是一个replace的过程。
第二步 编译及优化
命令: gcc -o test.s -S test.i
或者 /路径/cc1 -o test.s test.i
结果: 生成汇编文件test.s(可打开后查看源文件生成的汇编码)
作用: 通过词法和语法分析,确认所有指令符合语法规则(否则报编译错),之后翻译成对应的中间码,在linux中被称为RTL(Register Transfer Language),通常是平台无关的,这个过程也被称为编译前端。编译后端对RTL树进行裁减,优化,得到在目标机上可执行的汇编代码。gcc采用as作为其汇编器,所以汇编码是AT&T格式的,而不是Intel格式,所以在用gcc编译嵌入式汇编时,也要采用AT&T格式。
第三步 汇编
命令: gcc -o test.o -c test.s
或者 as -o test.o test.s
结果: 生成目标机器指令文件test.o(可用objdump查看)
作用: 把汇编语言代码翻译成目标机器指令, 用file test.o 可以看到test.o是一个relocatable的ELF文件,通常包含.text .rodata代码段和数据段。可用readelf -r test.o查看需要relocation的部分。
第四步 链接
命令: gcc -o test test.o
或者 ld -o test test.o
结果: 生成可执行文件test (可用objdump查看)
作用: 将在一个文件中引用的符号同在另外一个文件中该符号的定义链接起来,使得所有的这些目标文件链接成为一个能被操作系统加载到内存的执行体。(如果有不到的符号定义,或者重复定义等,会报链接错)。用file test 可以看到test是一个executable的ELF文件。
当然链接的时候还会用到静态链接库,和动态连接库。静态库和动态库都是.o目标文件的集合。
静态库:
命令:ar -v -q test.a test.o
结果: 生成静态链接库test.a
作用: 静态库是在链接过程中将相关代码提取出来加入可执行文件的库(即在链接的时候将函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中),ar只是将一些别的文件集合到一个文件中。可以打包,当然也可以解包。
动态库:
命令: gcc -shared test.so test.o
或者/PATH/collect2 -shared test.so test.o (省略若干参数)
结果: 生成动态连接库test.so
作用: 动态库在链接时只创建一些符号表,而在运行的时候才将有关库的代码装入内存,映射到运行时相应进程的虚地址空间。如果出错,如找不到对应的.so文件,会在执行的时候报动态连接错(可用LD_LIBRARY_PATH指定路径)。用file test.so可以看到test.so是shared object的ELF文件。
当然以上各步可以一步或若干步一起完成,如gcc -o test test.c直接得到可执行文件。
附:
ELF文件格式
ELF文件格式是ABI(Application Binary Interface)的一部分,被Tool Interface Standards committee作为在32位Intel架构下可移植的目标文件格式。其格式比较复杂,这里就不细讲了,只说说其类型。
在specification 1.1中定义了的类型。表示在ELF header中的e_type
Name Value Meaning
==== ===== =======
ET_NONE 0 No file type
ET_REL 1 Relocatable file
ET_EXEC 2 Executable file
ET_DYN 3 Shared object file
ET_CORE 4 Core file
ET_LOPROC 0xff00 Processor-specific
ET_HIPROC 0xffff Processor-specific
主要的有4种
1. Relocatable file 保留了代码和数据,被用来和其他的object file一起创建可执行的文件或者是shared object file. (也就是我们常见的.o文件)
2. Executable file 保留了用来执行的程序,该文件可以被系统exec()加载用以创建程序进程。(也就是我们常说的可执行文件)
3. Shared object file 保留了代码和数据,以在两种情况下被连接,一是link editor如ld,可以用它与其他的Relocateble或者Shared的object file一起创建另一个object file. 二是与Executable file或者其他的Shared object file动态链接成为一个进程映像。(也就是我们常说的动态链接库,或者.so文件)
4. Core file 的内容在规范中没有指明,目前多用来记录core dump信息。
分享到:
相关推荐
现代编译器通常包含多种优化技术,如循环展开、死代码消除、常量折叠等,以提高程序运行效率。 五、编译器的种类 常见的C语言编译器有GCC(GNU Compiler Collection)、Clang、Microsoft Visual C++等。它们在遵循...
《现代编译原理》是一本深入探讨编译器设计的经典教材,由Alex A. Aiken等人撰写,通常被称为“虎书”。这本书详细介绍了编译器的构造过程,包括词法分析、语法分析、语义分析、优化以及目标代码生成等核心步骤。...
6. **现代编译器流程**:源代码经过预处理器、编译器、汇编器和链接器四个主要阶段,最终生成可执行程序。这个过程中,编译器不仅负责转换语法,还可能进行优化,以提升程序性能。 了解这些基础知识有助于程序员更...
3. **反编译过程**:反编译器首先解析.class文件中的字节码,然后根据JVM规范重建出类的结构,包括类名、方法、变量等。接着,它尝试恢复源代码的语句和表达式结构,这个过程可能不完美,因为字节码可能丢失了某些源...
5. **错误处理**:TASM5 在编译过程中会检查语法错误和逻辑错误,帮助程序员定位并修复问题。 6. **优化**:虽然汇编语言已经是较低级别的编程语言,但编译器仍可以提供一些简单的优化选项,如消除冗余指令、代码...
编译过程涉及词法分析、语法分析、语义分析、优化以及目标代码生成等多个阶段。 #### 二、词法分析(词法扫描) 词法分析是编译过程的第一步,主要任务是从源程序中识别出一个个有意义的单词符号(token)。例如,...
现代编译器还引入了诸如自动垃圾回收、类型推断和延迟绑定等高级特性。这些技术在提升编程效率和代码质量方面发挥了重要作用。 在实践中,编译器设计者还会运用到编译器构造工具,如ANTLR、Flex和Bison,它们可以...
对于大多数现代编译器而言,编译阶段生成的中间代码通常是汇编语言。这一阶段的任务是将汇编语言翻译成机器语言。例如,在Intel x86架构下,“MOV AX, BX”这条汇编指令会被转换成相应的机器码,如“35 01 10”。 #...
依赖软件的安装为编译过程提供了必要的支持,如UEFI和UBOOT的特定实现。这些依赖项通常包括系统引导加载器、固件以及可能的其他中间件等,它们对于系统的启动和运行至关重要。 在配置过程中,编译UBOOT和UEFI是两个...
内存管理是现代编译器不可或缺的部分,涉及内存分配、垃圾回收和内存对齐等话题。其中,垃圾回收机制自动追踪并释放不再使用的内存,防止内存泄漏。编译器可以采用不同的垃圾回收策略,如引用计数、标记-清除、复制...
词法分析是将源代码分解为词素(tokens)的阶段,它是编译过程的第一步,帮助识别程序中的关键字、标识符、常量等基本元素。语法分析则根据源代码的词素构造语法树,理解程序的结构。语义分析进一步检查程序的逻辑...
在深入探讨ARM架构下的常见编译错误之前,我们需要先了解一些基本概念。ARM(Advanced RISC Machines)是一种基于精简指令集计算机(RISC)设计的处理器架构,广泛应用于嵌入式系统、移动设备和服务器等领域。STM32...
配置和编译过程中需确保g77和GCC的版本兼容,通常需要先安装GCC,然后构建并安装g77。 其次,Intel Fortran Compiler(ifort)是Intel公司提供的商业编译器,它支持Fortran 77、Fortran 90/95/2003/2008等多个标准...
源码编译过程通常包括获取源代码、配置构建系统、编译源代码和链接生成可执行文件。对于方舟编译器,开发者需要知道如何使用GN构建系统,以及如何指定编译器路径。 **现代C++特性**: 方舟编译器可能利用了C++的...
- 这个选项可以确保编译过程中尽可能多地报告潜在的问题,帮助开发者避免因忽略小错误而导致的大问题。 2. **-O[0-3]**:设置优化级别。 - `-O0` 不启用优化。 - `-O1` 默认优化级别,提供适度的优化,同时保持...
**标题:“很老的Watcom编译器”** 在编程世界中,编译器是将高级语言转换为机器可理解的二进制代码的关键工具...尽管现在有更现代的编译器和开发环境,但Watcom编译器的历史价值和它在编译技术发展中的地位不容忽视。