相信读者已经看过了Intel平台下Linux中ELF文件动态链接的加载、解析及实例分析(一): 加载的内容了,了解了ELF文件被加载的时候所经历的一般过程。那我们现在就来解决在上一篇文章的最后所提到的那几个问题,以及那些在dl_open_worker中没有讲解的代码。
由于源代码过分的冗长,并且由于效率的考虑,使原本很简单的代码变成了一件 TRAMPOLINE 的事情,所以我对它进行了大幅度的改变,不仅删除了所有不必要的代码,而且还用伪代码来展现它最初的设计思想。
|
先说明,其实加载一个动态链接库的依赖动态链接库不是一件简单的事,因为所有的动态链接库可能还有它自己所依赖的动态链接库,如果采用递归简单方法实现不仅是不可能的-----因为你可以参看第一篇的文章,那里提到了一个在加载动态链接库中的加锁问题,而且也是没有必要的,你并不能保证这样的动态链接库依赖关系会不会形成一个依赖循环,就像下面的一张图所显示的那样:
这样最简单的想法就是我们不重复的加载所有的动态链接库,这里就用一个单链实现-----在原来的程序中也是用这个方法,但那里用来分配的方法是在栈中直接实现,这样可以加快程序的运行,但程序可读性大大减弱了。
23 行就首先就把 lmap 自己加入这个 struct list 中去,在 26 行的 for_each_in_list(add_list,curlmap) 其实是就是把 curlmap=curlmap->next,并判断它的 curlmap!=NULL,
28 行的 for_every_DT_NEEDED_section(curlmap,needed_dyn)
主要就是 needed_dyn=curlmap->l_info[DT_NEEDED]; 但这里要注意的是,在一个动态链接库中可能有不只一个,就像在 readelf -a 的例子
|
更确切的是要在 lmap-> l_ld 的 dynamic section 中查找它的 d_tag 为 DT_NEEDED 中
30 行的 get_needed_name 用的方法是这样的
|
很明显这里就会把这个动态链接库映射来完成它的加载,而 35 行是要把 add_list 扩充,这里只会对同一个动态链接库加载一次,所以不会有前面的循环加载,再回过头来看 26 行到 37 行之间的那个循环,如果在 35 行中加入了那个没有重复的动态链接库。那整个循环就可能继续循环下去。
从 39 行到 51 行之中就把这个函数中已经得到的依赖动态链接库 copy 入 l_searchlist 与 l_initfini 这两个的重要数组中, 巧妙的是它们采用了一起分配的。最后前面的那个临时单链表。
在学习汇编语言的时候,我们对不同的寻址方式肯定有很深的印象。但对于在汇编语言中同样重要的转移指令,只是一笔带过(用到了call 与 jxx ----------- 这里的 jxx 是指如 jmp jae jbe 这样的有条件转移指令和无条件转移指令)。然而,如果讲到动态链接库的链接实现则一定要提到这一内容。
所谓相对转移,就是这个二进制代码的中的它是可以在重定位的环境中不经修改,就可以运行的。如下面的情况,
|
变成一般的地址是这样的
|
这里旁边的 719 就是这个 ELF 文件与起始地址相比的偏移量,而在里面的 e9 e2 fe ff ff 如果写成看的往后退 0x11e 因为这是 ff ff fe e2(intel 是 little endian 表示方法)所表示的 -0x11e 的数。如果把 719 加上 5 再减去 600 就是这个数了。这便是处理器的相对转移。
还有另一种转移方式,就是绝对转移。
|
这个如果用最简单的代码来表示是
|
很明显&