阅读目录
- 动态链接器的自举
- 装载共享对象
- 符号的优先级
- 全局符号介入与地址无关代码
- 重定位与初始化
- linux动态链接器的实现
正文
1. 动态链接器的自举
我们知道动态链接器本身也是一个共享对象,但是事实上它有一些特殊性。
对于普通共享对象文件来说,它的重定位工作由动态链接器来完成。
他也可以依赖其他共享对象,其中的被依赖共享对象由动态链接器负责链接和装载。
可是对于动态链接器来说,它的重定位工作由谁来完成?它是否可以依赖于其他共享对象?
这是一个“鸡生蛋,蛋生鸡”的问题,为了解决这种无休止的循环,动态链接器这个“鸡” 必须有些特殊性。
首先是,动态链接器本身不可以依赖于其他任何共享对象;
其次是动态链接器本身所需要的全局和静态变量和重定位工作由它本身完成。
对于第一个条件我们可以认为的控制。
在编写动态链接器时必须保证不使用任何系统库,运行库;
对于第二个条件,动态链接器必须在启动时有一段非常精巧的代码可以完成这项艰巨的工作而同时又不能使用全局和静态变量。
这种具有一定限制条件的启动代码往往被称为自举(Bootstrap
)。
动态链接器入口地址即是自举代码的入口,当操作系统将进程控制权交给动态链接器时,动态链接器的自举代码即开始运行。
自举代码首先会找到它自己的GOT
。而GOT
的第一个入口保存的是“.dynamic
”段的偏移地址,由此找到了动态连机器本身的“.dynamic
”段。
通过“.dynamic
”的信息,自举代码便可以获得动态链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,先将它们全部重定位。
从这一步开始,动态链接器代码中才可以使用自己的全局变量和静态变量。
实际上在动态链接器的自举代码中,除了不可以使用全局变量和静态变量之外,甚至不能调用函数,即动态链接器本身的函数也不能调用。
这是为什么呢?其实我们在前面分析地址无关代码时已经提到过,实际上使用PIC
模式编译的共享对象,对于模块内部的函数调用也是采用跟模块外部函数调用一样的方式,即使用 GOT/PLT
的方式,所以在GOT/PLT
没有被重定位之前,自举代码不可以使用任何全局变量,也不可以调用函数。
下面这段注释来自于 Glibc26.1
源代码中的 elf/rtld.c
这段注释写在白举代码的末尾,表示自举代码已经执行结束。“ Now life is sane
",可以想象动态链接器的作者在此时大舒一冂气,终于完成白举了,可以自由地调用各种函数并且随意访问全局变量了,
2. 装载共享对象
完成基本自举以后,动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表当中,我们可以称它为全局符号表( Global Symbol Table
)。
然后链接器开始寻找可执文件所依赖的共享对象,我们前面提到过“.dynamic”段中,有一种类型的入口DT_NEEDED,它所指出的是该可执行文件(或共享对象)所依赖的共享对象。
由此,链接器可以列出可执行文件所需要的所有共享对象,并将这些共享对象的名字放入到一个装载集合中。
然后链接器开始从集合里取个所需要的共享对象的名字,找到相应的文件后打开该文件,读取相应的ELF文件头和“ .dynamic”