前言
平时写程序,写完之后就是直接点一下编译再运行,就没我们什么事了;但是我们在点编译的时候,大多数软件是在默默调用GCC,那么这背后到底做了什么呢?
一、GCC编译
在linux下使用gcc–help查看帮助
参数 | 对应操作 |
---|---|
-E | 仅预处理,不编译、汇编和连接 |
-S | 预处理和编译,不汇编、链接 |
-c | 预处理、编译和汇编,不链接 |
-o | 指定输出文件名 |
图片来源:https://www.cnblogs.com/yanFlyBlog/articles/14988043.html
总结
:在编译过程中,使用"-c",“-S”,或"-E"选项可使gcc编译停止在相应阶段,否则编译一步到位生成可执行文件。
二、编译一个小程序
- 代码如下
#include <stdio.h>
int main()
{
printf("hello,world");
return 0;
}
1.预处理
处理以#
开头的预处理指令,如宏定义和所包含头文件,将被包含的文件插入到对应预处理指令的位置去(此阶段不检查语法)。
- 在命令行中输入
gcc -E hello.c -o hello.i
预处理之后,将<stdio.h>
中所包含文件展开,可使用普通文本文件打开
2.编译
编 译 过 程 就 是 对 预 处 理 完 的 文 件 进 行 一 系 列 的 词 法 分 析 , 语 法 分 析 , 语 义 分 析 及 优 化 后 生 成 相 应 的 汇 编 代 码。
编译就是把C/C++代码(比如上面的".i"文件)“翻译”成汇编代码
- 命令行中输入
gcc -S hello.i -o hello.s
注意:
在生成汇编文件时,要注意命令行参数-S为大写,而如果输入为小写的话,用文本编辑器打开为乱码;并且在后面生成.o文件时会出错。
其实是因为输入参数区别大小写,当输入为小写时,gcc编译过程没有被截断,直接生成了可执行文件,可直接运行,所以在输入过程中要注意。
3.汇编
- 生成可重定位文件 hello.o
gcc -c hello.s -o hello.o
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码,生成可重定位文件(一个不带地址信息,ELF格式的二进制文件),包含多个section部分。
- 可使用Linux 命令readelf查看节区信息
readelf -S hello.o
- 关于ELF格式文件详情可参考
- 查看生成的hello.o
hexdump -C hello.o
对于可重定位文件,它里面的代码与数据,都是各个文件独立的代码与数据,在一个工程中,可能会存在多个C文件,每个C文件都会被首先编译生成一个可重定位文件,然后经过链接器将这些可重定位文件进行链接,从而生成最终的可执行文件。
4.生成可执行文件
- 执行命令,生成可执行文件
gcc hello.c -o hello
或
gcc hello.o -o hello
默认使用动态库链接
- 可使用命令查看文件所依赖的动态库
使用命令利用静态库生成可执行文件
gcc -static hello.c -o hello1
- 查看两者大小
有关静态库和动态库的生成与使用,可参考我的另一篇博客
三、对比分析
对于可重定位文件(没有地址信息)
- 各个段没有具体的起始地址,只要段大小信息
- 各个标识符没有实际地址,只有在段中的偏移地址(相对地址)
- 段和标识符的实际地址都需要链接器具体制定,这也是链接器的主要作用
对于可执行文件
- 各个段有自己的起始地址,这些地址就是将来要被加载到内存中的地址(虚拟内存),有了起始地址,才能加载到内存,这是可执行文件与可重定位文件一个区别
- 可执行文件中的各个符号,都有了正确的地址,以及符号被引用的地方也正确填上了符号的地址
内容来源
总结
以上是我的一点学习体会,有很多不足之处,关于gcc 的编译过程还有许多地方需要学习,后期在不断学习过程中会不断改进,若有问题欢迎大家留言,一起学习进步,不胜感激!!!
可参考书籍:
嵌入式C语言自我修养:从芯片、编译器到操作系统/王利涛编著 电子出版社
深入理解计算机系统深入理解计算机系统(原书第3版) 机械工业出版社