为什么需要动态链接
每个App都是一个独立的“沙盒”,如果沙盒A以及沙盒B都需要引入同一个第三方文件,如果每一个“沙盒”(进程),都引入一个则会占用内存,动态链接就是为了解决这种情况而生的。动态链接使得所有内存区域中只有一份第三方文件,如libc,比如A进程需要引入libc,linker就会在libc加载的时候读取其一些字段,比如plt、rel表等,把A进程需要的函数地址进行赋值。这样做的好处是可以减少空间、增加运行效率。但是linker是怎么判断需要加载什么函数呢?这就需要elf文件格式进行解析。
elf文件格式浅析
框出来的这两个是字符串相关的
.dynsym
该属性记录着动态链接的字符串
.dynstr
该属性记录着整个so的所有字符串
.hash
用于索引字符串的,以使得能快速加载字符串。字符串发生变化,则哈希表也会发生变化。
.rel.dyn
.rel.plt
.plt
.text
该属性用于储存代码。比如bl指令需要加载一些指令,都会放到.text属性里。
.data.rel.ro
.data
存放已初始化的全局变量、常量。
.bss
存放未初始化的全局变量,所以此数据均为0,仅做占位。
.rodata
只读数据段,此段数据不可修改,存放常量。
.init_array和fini_array
在linker执行的时候才会执行的两个字段,程序运行时会执行.init_array中的指令,程序退出时会执行.fini_array中的指令
带有.rel的属性
带有.rel属性的都与重定位相关。动态链接时是需要用到的。
rel表
rel表会分为很多种,用于告诉linker哪些函数需要重定位。但不是最终指向那个函数,最终指向函数地址是储存于.got表当中的。
.rel.plt
用于保存需要重定位函数的符号。
.rel.dyn
用于保存重定位函数的指针,例如有一行代码int a* = A;
而该a变量指向了别的so文件的A函数指针地址,此时,能成功指向这个A函数,主要就依靠.rel.dyn表进行的。
.got表
.got表存储着所有重定位函数的地址。
.plt表
该表主要用于跳转定位到.got表执行.got表里储存的对应位置的函数。但是plt也分函数跳床和指令跳床,