本文,解析Linux C/C++ 可执行程序的编译链接。
编译全过程:
hello.c —1— hello.i —2— hello.s —3— hello.o —4— a.out/hello
细化步骤解剖:
(1)预处理:gcc -E hello.c -o hello.i
-E只对文件进行预处理,不编译汇编、链接
编译器对各种预处理命令进行处理:头文件包含、宏定义扩展、条件编译选择等
hello.c(C源文件)——hello.i(C源文件)
file hello.i输出:
hello.i: C source, ASCII text
(2)编译:gcc -S hello.i
-S只对文件进行编译,不汇编、链接
将预处理得到的源文件,进行“翻译转换”,产生出机器语言的目标程序
hello.i(C源文件)——hello.s(汇编文件)
file hello.s输出:
hello.s: assembler source, ASCII text
(3)汇编:gcc -c hello.s
-c只对文件进行编译,不链接
将汇编文件翻译为可重定位文件(relocatable)
hello.s(汇编文件)——hello.o(relocatable)
file hello.o输出:
hello.o: ELF 32-bit LSB
relocatable
, Intel 80386, version 1 (SYSV), not stripped
(4)链接:gcc hello.o -o hello
进行链接,生成可执行文件
处理可重定向文件,把各种符号引用和符号定义转换为可执行文件中的合适信息(虚拟地址)
hello.o(relocatable)——a.out/hello(可执行文件)
file a.out输出:
ELF 32-bit LSB
executable
, Intel 80386, version 1 (SYSV),
dynamically linked
, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d4bf39d5b3e516e739864f738d408212334f9d35, not stripped
PS:上述根据编译链接的整体过程,不断的推进,所以彼此间的文件出现:“下一步依赖上一步”的关系。
解析
- (1)步骤的-o选项,是表示-E选项操作输出到hello.i文件中,-o意为output,确定输出文件名字,同理(4)步骤也是
- .o称为可重定向文件(relocatable),是因为<链接>将把地址转化为虚拟地址,提供程序链接运行使用。
- 实际开发:
- 直接生成xxx.o、或链接库xxx.so/xxx.a
- 链接依赖库,生成a.out
补充:
静态链接
gcc hello.o -static -o hello_static
file hello_static:
hello_static: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux),
statically linked
, for GNU/Linux 2.6.32, BuildID[sha1]=e3b978f18ef711ce682a6c1868317cbde1d05adf, not stripped
参考链接
后期改进
- 实际上,gcc/g++编译器是非常复杂的应用程序,堪比OS,同时这也是泛指的“编译器”,实际上是编译器+链接器的结合,甚至还有其他更加复杂的功能在里面,我们暂时理解到这个层面就可以了。日后需要使用到,再去深入理解。
拓展:
实际上,理解了整个编译链接过程,还需要继续深入到执行文件,研究执行文件的组成,真正观察“编译器”和“链接器”的工作成果。
[Linux]C程序的存储空间布局