目录
在Linux下使用gcc来编译hello.c程序
1、gcc的具体使用方式
2、gcc编译链接过程分解
一、预编译:.i
二、编译:.s
三、汇编:.o
四、链接: .exe
五、运行: 指令和数据
编译:就是把文本形式源代码翻译成机器语言形式的目标文件的过程 链接:是把目标文件,操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码的过程
源文件生成最终的可执行文件分:预处理(Prepressing)、编译(Compilation)、汇编(Assembly)、链接(Linking)等四步
预处理,生成预编译文件(.i文件)
gcc -E hello.c -o hello.i
编译,生成汇编代码(.s文件)
gcc -S hello.i -o hello.s
汇编,生成目标文件(.o文件)
gcc -c hello.s -o hello.o
链接,生成可执行文件
gcc hello.o -o hello
源代码文件hello.c和相关的头文件,如stdio.h被与编译器cpp预编译成.i的文件;
gcc -E hello.c -o hello.i // -E表示只进行预编译预编译指令
预编译过程主要处理那些源代码文件中的以“#”开始的,如“#include”、“#define”等,处理规则如下:
(1)删除所有的“#define”,并且展开所有的宏定义(文本替换);
(2)处理所有的条件预编译指令,“#if”,”ifdef“,“ #endif”,“#elif”,“#else”;
(3)处理"#include"预编译指令,将被包含的文件插入到改预编译指令的位置,该过程递归进行,也就是说被包含的文件可能还包含其他文件(递归展开#include);
(4)删除所有的注释“//”和“/* */”;
(5)添加行号和文件名标识符,以便于编译器产生调试用的符号信息及编译时产生编译错误和警告时显示行号;
(6)保留所有的#pragma编译器指令,因为编译器需要使用它们;
经过编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件已经被插入到.i文件中。所有,我们无法判断宏定义是否正确时,可以查看预编译后的文件来确定问题
编译是程序构建的核心部分,也最复杂。需要在这个过程完成词法分析,语法分析,语义分析及优化产生的汇编代码
gcc -S hello.i -o hello.s //生成汇编指令代码
或者 gcc -S hello.c -o hello.s
(1)词法分析
(2)语法分析
(3)语义分析
(4)代码优化
(5)生成汇编指令(汇总符号)
编译过程通过词法和语法分析,确认所有指令符合语法规则(否则编译报错),之后翻译成对应的中间码,在Linux中被称为RTL(Register Transfer Language),通常是平台无关的,这个过程也被称为编译前端。编译后端对RTL树进行裁剪,优化,得到在目标机上可执行的汇编代码。gcc采用as作为其汇编器,所以汇编代码是AT&T格式的,而不是Inter格式,所以在用gcc编译嵌入式汇合在一起编时,也要采用AT&T格式
现在一般gcc把预编译和编译都合在一起了,但是也可以分开实现,gcc的命令其实是对一些编译程序的包装而已,它根据不同的参数去调用预编译程序(ccl)、汇编器(as)、链接器(ld)
汇编器将上面生成的汇编代码转换成机器可以执行的指令。每一条汇编语句对应一条机器指令。所以汇编过程比编译过程简单,没有复杂的语法、语义、也不需要优化指令。只是根据汇编指令和机器指令的对照表一 一翻译就可以了,“汇编”这个名字也来源于此,上面的汇编过程我们可以调用汇编器as来完成,最终输出的是目标文件.o
as hello.s -o hello.o
或者 gcc -c hello.s -o hello.o //生成可重定位的二进制目标文件
或者 gcc -c hello.c -o hello.o
(1)将汇编指令翻译成二进制格式,生成各个section,生成符号表(翻译指令)
由于汇编产生的hello.o的文件内容为机器码,不能以文本形式方便的呈现
链接器ld将各个目标文件组装在一起,解决符号依赖,库函数依赖,并生成可执行文件。链接的时候还会用到静态链接库和动态链接库。静态链接库和动态链接库都是.o目标文件的集合
动态库在链接时只创建一些符号表,而在运行的时候才将有关库的代码装入内存,映射到运行时相应进程的虚地址空间。如果出错,如找不到对应的.so文件,会在执行的时候报动态连接错(可用LD_LIBRARY_PATH指定路径)
gcc -o hello hello.o ----> 生成最终的可执行文件hello
或者gcc -o hello hello.c
直接通过源文件生成可执行文件(即就是执行 : gcc -o hello hello.c)
(1)合并段和符号表 //符号表:两强报错,一强一弱选强
(2)符号解析
(3)分配地址和空间
(4)符号的重定位(链接的核心)
(1)创建虚拟地址空间和物理内存的映射(内核映射结构体) 创建页目录,页表
(2)加载指令和数据 //内存是处理器,指令由cpu执行,cpu的核心是算术逻辑单元
(3)入口地址写入下一行指令寄存器