链接是将各种代码和数据部分收集起来组合成一个单一的文件的过程,这个文件可被加载(或呗拷贝)到存储器并执行。分离编译(将应用程序分解为一个个小的模块,独立的修改和编译这些模块,在从新编译、连接应用)。
7.2静态链接
1. 符号解析:将目标文件中的每个全局符号都绑定到唯一的定义。
汇编:是指用汇编语言直接操作机器,用汇编语言编写的程序不需要编译器进行编译;
编译:指把高级语言编写的程序翻译成机器语言,让机器执行不同的高级语言有不同的编译器;
编译器的作用:将编写的语言(C语言或汇编语言等)直接翻译成机器可识别的语言;
汇编器的作用:一般生成目标代码,需要进过连接器生成可执行代码方可执行;
2. 重定位:确定每个符号的最终存储地址,并修改对目标的引用。
7.3目标文件
目标文件分为三种:
1. 可重定位目标文件:(包含二进制代码和数据,创建可执行目标文件)
2. 可执行目标文件:(包含二进制代码和数据,拷贝到存储器执行)
3. 共享目标文件:(特殊可重定位目标文件,可咋加载/运行时被动态加载到存储器执行)
图:可重定位的目标文件
7.5符号和符号表
每个可重定位目标模块m都有一个符号表,包含m所定义和引用的符号信息,三种符号:
1. 有m定义并能被其他模块引用的全局符号;
2. 其他模块定义,m引用全局符号;
3. 自被m定义和引用的本地符号。
Typedefstruct{
Int name;//字符串表字节偏移
Int value;//符号地址
Int size;
Char type:4,binding:4;
//表示符号是本地还是全局
Char reserved;
Char section;
//符号与某个节相关联
}Elf_Symbol;
7.6符号解析
将每个引用和它输入的可重定位目标文件的符号表中的一个确定符号定义连接起来;
1. 解析重定义全局符号:
强:函数和已经初始化的全局变量;
若:未初始换的全局变量;
l 规则1:不允许多个强符号;
foo1.c
int main()
{
return 0;
}
bar1.c
int main()
{
return 0;
}
Foo2.c bar2.c
Int x =3; int x =2
Int main() int f()
{ {
} }
l 规则2:一个强多个若,选择强;
Foo3.c bar3.c
Int x =3; int x
Int main() int f()
{ {
F(); x =2;
Printf(“%d”,x); }
}//x = 2
l 规则3:多个若,从若中选择一个;
2. 与静态库连接:
测试程序:
___________________addvec.c
void addvec(int*x,int*y,int*z,int n )
{
int i;
for(i =0; i < n; i++)
z[i]= x[i]+ y[i];
}
___________________addvec.c
__________________multvec.c
void multvec(int*x,int*y,int*z,int n)
{
int i;
for(i =0; i < n; i++)
z[i]= x[i]*y[i];
}
___________________multvec.c
___________________main2.c
#include<stdio.h>
#include"vector.h"
int x[2]={1,2};
int y[2]={3,4};
int z[2];
int main()
{
addvec(x,y,z,2);
printf("z = [%d %d]\n",z[0], z[1]);
return0;
}
__________________main2.c
Step1:gcc –c addvec.c multvec.c
Step2:ar rcs libvector.a addvec.o multvec.o
Step3:gcc –o2 –c main2.c
Step4:gcc –static -0 p2 main2.o ./libvector.a
3. 静态库解析引用:
7.7重定位
组成:
l 重定位节中符号定义:链接器将相同类型的节合并,运行时赋予新的地址;
l 重定位节中符号引用:链接器修改代码和数据节中对每个符号的引用,指向正确的运行时地址。(以来重定位条目 (即 可重定位目标模块)中的数据结构)
7.8可执行目标文件
7.9可加载执行目标文件
调用execve函数调用加载器
代码段:总是从0x0804800处开始
数据段:在接下来的下一个4KB对齐的地址出
还有一个段为共享库保留
7.10动态链接共享库
Step1:gcc –shared –fPIC –o libvector.so addvec.c multvec.c
Step2:gcc –o p2 main2.c ./libvector.so
7.11从应用程序中加载和链接共享库
__________________dll.c
#include<stdio.h>
#include<stdlib.h>
#include<dlfcn.h>
int x[2]={1,2};
int y[2]={3,4};
int z[2];
int main()
{
void*handle;
void(*addvec)(int*,int*,int*,int);
char*error;
handle = dlopen("./libvector.so",RTLD_LAZY);//加载和链接共享库
if(!handle){
fprintf(stderr,"%s\n",dlerror());
exit(1);
}
addvec = dlsym(handle,"addvec");//函数的输入是前面已经打开共享库的句柄和一个符号名字
if((error = dlerror())!= NULL){
fprintf(stderr,"%s\n",error);
exit(1);
}
addvec(x,y,z,2);
printf("z = [%d %d]\n",z[0],z[1]);
if(dlclose(handle <0)){//卸载共享库
fprintf(stderr,"%s\n",dlerror());
exit(1);
}
return0;
}