内功心法:
如果想理解大型程序的构建必须完全理解linker阶段到底做了哪些工作;一下抛砖引玉,一些个人的理解。
提前知识:linxu下目标文件格式(ELF):
1.可重定位目标文件; //你可以暂时理解为通常说的.o
2.可执行目标文件; //你可以单纯的理解为.exe
3.共享库;
首先对于基本的程序编译步骤大致可以分为如下:
比如正常编译一个a.c文件:
gcc -c a.c // 产生a.o
gcc -o a a.o // 产生a(ELF)
预编译:
cpp a.c a.i; //简单的宏替换;
编译: //将进行宏替换之后的文件转换成特定的汇编文件;
cc1 a.i -o a.s
汇编:
as -o main.o main.s //将汇编好的文件转化成对应的.o格式文件;//主要是为了连接程序便利。
连接:(简单的说就是将一堆.o 生成.a .so 或者.out分别对应静态库动态库以及可执行文件)
假设有如下三个 a.o , b.o, c.o; main.o(使用a.o , b.o, c.o,并且由主入口函数,也就是所谓的main)则对应的编译
main: gcc -Wall -g -o main main.o a.o b.o c.o; //-g指示可调式, -Wall指示出错显示更多错误信息;
lib.a: ar rcs a.o b.o c.o;
lib.so:
//注意编译.so的时候一定需要带的-fPIC选项
gcc -g -c -fPIC -Wall a.c b.c c.c
gcc -g -c -shared -o libfoo.so a.o b.o c.o
注:还需要掌握的是ELF文件格式:
![这里写图片描述](https://img-blog.csdn.net/20180604112743499?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpNzc1MDg1NzM3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
至于各个节的意义需要自己掌握,对于理解程序的编译运行包括库的制作都很重要。
进入正题:
理解ELF文件格式之后:
对应的每一节都是os所说的符号表示,也就是让os或者在连接时候可以通过制定的符号,找到你想要的信息:
符号主要分为如下三类:
1. 全局(全局函数 以及 变量)
2 . 局部(static定义的函数以及变量,仅供内部调用)
3. 外部 (供外部调用的函数)
linux下静态库主要的好处就是尽可能的减少了代码所占磁盘 内存的空间,最简单的例子就是比如c语言所定义的一大堆标准库,你在实际的编译的时候,你的可执行代码都会把你所引用的标准库中所有函数加进去即使你只用到了一个,静态库由此而生,其实也就是对应符号的外部引用;
而静态库的应用归根到底就是围绕着外部引用做文章,由此在实际的编译过程中也产生了一系列的问题:
比如如下过程:
gcc -c a.c b.c
ar rcs lib.a a.o b.o ; // 生成.a
gcc -o main main.o lib.a // main.c 调用lib.a
连接器主要完成的工作如下:(姑且把main称作目标文件, lib.a称为存储文档)
link解析时主要有如下三个集合:
E: 存放用于生成最后可执行文件的 符号集合;
U:(引用但尚未定义的符号集合)
D:(支持外部引用的符号集合)
在执行上述操作的时,主要完成以下,link从左往右读取main.o发现是目标文件,则将其存放入E集合中,并且把引用尚未定义的放入结合U中,以及定义的放入D中,接着扫描lib.a,把支持外部引用的部分放到D中,并与E中相比较,如果出现引用但尚未定义的,则把lib.a中的相应符号部分放入E中舍去其余的部分,这样就起到了只复制调用部分。
也正是因为执行流程如下,所以需要特别注意编译连接时候的文件的顺序,如果把a依赖于b则必须把b放在后面,如果把b放在了前面则会导致U集合为空,而后面再导入a的时候出现undefined reference的错误。
特献上全部流程:
a.cpp内容如下:
int atime = 0;
int sum(int a,int b){
atime++; //表示调用次数;
return a+b;
}
b.cpp内容:
int btime = 0;
int sum_func(int a,int b){
btime++;
return a+b;
}
func.h内容:
extern int sum(int a,int b);
extern int sum_func(int a,int b);
main.c内容:
#include<iostream>
#include"func.h"
using std::cout;
using std::endl;
int main(){
int result1 = sum(1,2);
int result2 = sum_func(1,2);
cout<<"result1 is"<<result1<<endl;
cout<<"result2 is"<<result2<<endl;
return 0;
}
编译过程:
g++ -c a.cpp b.cpp
g++ -c main.cpp
ar src libsum.a a.o b.o
g++ -static -o main main.o libsum.a
//g++ -static -o main libsum.a main.o
会出现如下问题:
unrefernced defined function: sum, sum_func;
![这里写图片描述](https://img-blog.csdn.net/20180604145559744?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpNzc1MDg1NzM3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)