目录
地址空间分配
两个链接的方式
按序叠加
相似节合并
静态链接的详细过程
虚拟内存
重定位文件
静态链接库
地址空间分配
我们把之前的两函数分为两个文件
main.c
extern int shared
extern vooid fun(int *a,int *b);
int main(){
int a=100;
func(&a,&shared);
return 0;
}
func.c
int shared =1;
int tmp=0;
void func(int *a,int *b){
tmp = *a;
*a=*b;
*b=tmp;
}
gcc -static -fno-stack-protector main.c func.c -save-temps --verbose -o func.ELF
两个链接的方式
这里我们先给出
按序叠加
我们如果要把main.o 和func.o 链接成一个可执行文件 最简单就是按序叠加
但是这种弊端 就是如果链接的目标文件过多 可执行文件就会巨大
而且一个不足一页的代码节或数据节 也要占一页的空间 这样就造成了浪费
相似节合并
把不同目标文件相同属性的节合并为一个节
main func的text 合并为一个新的text节
这种方式被现在的编译器所采用
先对各个节的长度 属性 和偏移进行分析
然后输入目标文件中的符号表的符号定义和符号引用统一成一个全局符号表
最后 读取输入文件的各类信息对符号进行解析 重定位等操作
相似节合并就出现在重定位当中
完成后 程序的每条指令和全局变量都有了运行的时候唯一的内存地址
静态链接的详细过程
为了构造可执行文件
编译器必须经过两个过程
符号解析
把每一个符号 (函数,全局变量,静态变量)的引用和其定义关联
重定位
把每一个符号定义和一个内存地址进行关联 然后修改这些符号的引用 让他指向地址
我们下面进行对比 可执行文件 func.ELF 和 中间产物 main.o的对比
我们重点关注 .text .bss .data
objdump -h func.ELF-main.o
main.o的
func.ELF的
objdump -h func.ELF
我们这里关注 VMA 和 LMA
VMA :虚拟地址
LMA :加载地址
这两个一般情况下是相同的
我们能发现 main.o的VMA和LMA是0 因为还没有进行链接
而func.ELF 有实际地址 因为相似节合并了 并且完成了虚拟内存的分配
虚拟内存
虚拟内存 就是 程序执行 是把代码RAM载入内存 如果一个代码很多 内存就饱满了
所以虚拟内存的作用就是 让机器认为还有很多内存 但是实际上 代码只是部分进入了内存中
还有一些在外存上 机器要使用外存代码了 就自动载入
百度百科:虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
接着我们使用objdump查看main.o的反汇编
objdump -d -M intel --section=.text func.ELF-main.o
只对text段进行反汇编
这里我们进行分析
main函数从 地址0开始
call函数的操作码是e8
后面是调取的函数地址 偏移量 但是没有进行重定位 所以0x000000000
函数的地址是在便宜处 0x2e的地方
我们可以通过下一条返回地址进行计算函数地址
mov的地址为2e
函数偏移量为 0x0
0x2e+0x0 = 0x2e 这只是一个临时地址
shared的地址在rip+0x0
其实编译器还根本不知道地址在哪 所以使用00000000代替
接着我们看看func.ELF的文件
objdump -d -M intel --section=.text func.ELF |grep -A 16 "<main>"
16 "<main>"找到main函数 并且打印前面的地址
main函数从 0x401745开始
call函数在 40176e的位置
mov为 401773 + 0x07 =0x4177A 这个就是func函数的地址
同时 rip+0xc398d 为 shared的地址
重定位文件
重定位文件中最重要的就是要包含重定位表 用于告诉链接器如何修改节的内容
例如
.rela.text 的节保存 .text的重定位表
.rela.text包含两个重定位入口
shared的类型为 R_X86_64_32 用于绝对寻址 cpu 直接把 指令编码中32位值作为有效地址
func的类型为R_X86_64_PC32 用于相对寻址 cpu 把指令编码中的32位值加上 PC的值得到下一条地址
objdump -r func.ELF-main.o
静态链接库
后缀名为.a 的为静态链接库文件 libc.a
一个静态链接库可以视为一组目标文件经过压缩打包的文件集合
执行各种编译任务的时候 需要有不同的目标文件
比如输入输出 printf.o scanf.o等
我们使用ar对目标文件进行压缩 编号 引用 这就形成了 libc.a
ar -t libc.a