链接和加载(linker and loader):
linker即链接器,它负责将多个.c编译生成的.o文件,链接成一个可执行文件或者是库文件;
loader即加载器,它原本的功能很单一只是将可执行文件的段拷贝到编译确定的内存地址即可,但是有了动态链接库以后,部分的外部库引用符号在加载的时候才会得到解析,所以加载也要处理链接器的相同操作重定位。
重定位原理:
符号表(Symbol Table):
符号表就是一张字符符号和地址的对应表,符号表的作用就是一个助记符,用一个字符串来标示某些抽象的地址,它能标示的地址有代码地址和数据地址,代码地址包括函数名、跳转标号,数据地址包括全局变量。
符号表的组织如下图所示:
符号表的作用就是将符号名称和地址进行绑定。而绑定的根本目的就是方便对符号的引用,在符号值发生改变的时候,不需要去手工改动源代码中对符号引用的地方,而这种改动是由链接程序在重新生成执行文件时自动完成的。
重定位表(Relocation):
有了符号表,就需要有人对符号表进行引用,在程序的执行过程中对全局变量的引用、跳转、调用函数,这些都涉及到相应的符号引用。符号和其引用是一对多的关系,一个符号可能被代码中多处引用。因为符号值改变的时候,也需要对所有引用符号的地方的代码进行修改,所以需要还有一张表来记录符号表的引用关系,这就是重定位表:
从上图可见,重定位表项用来记录链接和加载的过程中需要重新定位的位置,在各个段位置发生改变而引起符号地址改变时,根据重定位表来修改符号引用的值。
GOT表(Global Offset Table):
加载过程中的重定位,使用了一种改良的重定位手段:即通过两张间接访问表来屏蔽掉重定位带来的对代码的修改,访问外部数据使用GOT,访问外部程序使用PLT。这样可链接出位置无关代码PIC(Position Independent Code),需要重定位时只需要修改GOT和PLT的值,而不需要去改动可执行代码。
PLT表(Procedure Linkage Table):
加载过程中的重定位为了避免对代码的修改,引入了GOT来屏蔽对数据的访问,同理对外部代码的访问也是可以用GOT来访问的。但是为了实现动态链接的特性,即使用的时候才链接,不使用时可以不用链接,对外部代码的访问引入了一个新的表项PLT。
elf文件:
elf文件格式:
Linux环境下,三种类型的执行文件都可以使用elf格式来表示:可重定位文件(即编译生成但是未连接的文件)、动态库文件、可执行文件。
Elf文件提供了两种文件解析的视角,链接视角和动态加载视角。链接视角使用section的概念来解析文件,主要关注链接过程的使用;动态加载视角使用segment的概念来解析文件,主要关注加载和动态链接的实现。
整个文件的组织框图如上所示,ELF头描述了section header table和program header table的起始位置、表项大小和个数。根据section header table来寻址相应的section,根据program header table来寻址相应的segment,可以看到一般是一个segment包含多个section。
Linux下可以操作elf文件的有以下工具:
a.readelf
“readelf –a file“读出elf文件的所有信息。
b.nm
“nm file“读出elf文件的符号表信息。
c.objdump
“objdump –d file“反汇编出elf文件中包含可执行代码的section,elf命令中功能最强大的一个。
d.objcopy
转换elf文件为bin或者其他格式的文件,编译内核的时候会使用到。
e.strip
去掉elf文件中符号表和调试信息,对elf文件进行减肥。
f.addr2line
将绝对地址,转换成调试信息中的源文件行号。
elf文件中符号表和调试信息,对elf文件进行减肥。
f.addr2line
将绝对地址,转换成调试信息中的源文件行号。