转载自:IBM developerWorks 中国网站

王瑞川(jeppeterone@163.com)
从事 Linux 研发工作
2003 年 12 月

相信读者已看过了Intel平台下Linux中 ELF文档动态链接的加载、解析及实例分析(一): 加载的内容了,了解了ELF文档被加载的时候所经历的一般过程。那我们现在就来解决在上一篇文章的最后所提到的那几个问题,连同那些在dl_open_worker中没有讲解的代码。

一、_dl_map_object_deps 函数分析

由于源代码过分的冗长,并且由于效率的考虑,使原本很简单的代码变成了一件 TRAMPOLINE 的事情,所以我对他进行了大幅度的改变,不但删除了任何不必要的代码,而且还用伪代码来展现他最初的设计思想。


13 _dl_map_object_deps (struct link_map *lmap)
14
15 {
16
17 struct list_head* add_list;
18 char* load_dl_name;
19 struct link_map* curlmap;
20 Elf32_Dyn* needed_dyn;
21 struct link_map* new_lmap;
22 int lmap_count=1;
23
24 add_lmap_to_list(lmap,add_list);
25
26 for_each_in_list(add_list,curlmap)
27 {
28 for_every_DT_NEEDED_section(curlmap,needed_dyn)
29 {
30 load_dl_name= get_needed_name(curlmap,needed_dyn);
31
32 new_lmap=_dl_map_object(load_dl_name);
33
34
35 add_to_list_tail_uniq(add_list,new_lmap);
36 }
37 }
38
39 lmap_count=count_the_list(lmap);
40
41 lmap->l_initfini=(struct link_map**)malloc ((2*lmap_count 1)*(struct link_map*));
42
43 lmap->l_searchlist.r_list=&lmap->l_initfini[lmap_count 1];
44 lmap->l_searchlist.r_nlist=lmap_count;
45
46
47 copy_each_add_list_to_searchlist(lmap,add_list,lmap_count);
48
49 free_the_add_list(add_list);
50
51 }

先说明,其实加载一个动态链接库的依赖动态链接库不是一件简单的事,因为任何的动态链接库可能更有他自己所依赖的动态链接库,假如采用递归简单方法实现不但是不可能的-----因为您能够参看第一篇的文章,那里提到了一个在加载动态链接库中的加锁问题,而且也是没有必要的,您并不能确保这样的动态链接库依赖关系会不会形成一个依赖循环,就像下面的一张图所显示的那样:

这样最简单的想法就是我们不重复的加载任何的动态链接库,这里就用一个单链实现-----在原来的程式中也是用这个方法,但那里用来分配的方法是在栈中直接实现,这样能够加快程式的运行,但程式可读性大大减弱了。

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 的例子


Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libstdc -libc6.2-2.so.3]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libc.so.6]


更确切的是要在 lmap-> l_ld 的 dynamic section 中查找他的 d_tag 为 DT_NEEDED 中

30 行的 get_needed_name 用的方法是这样的


load_dl_name=curlmap->l_addr need_dyn->d_un.d_ptr curlmap->l_i
nfo[DT_STRTAB];


很明显这里就会把这个动态链接库映射来完成他的加载,而 35 行是要把 add_list 扩充,这里只会对同一个动态链接库加载一次,所以不会有前面的循环加载,再回过头来看 26 行到 37 行之间的那个循环,假如在 35 行中加入了那个没有重复的动态链接库。那整个循环就可能继续循环下去。

从 39 行到 51 行之中就把这个函数中已得到的依赖动态链接库 copy 入 l_searchlist 和 l_initfini 这两个的重要数组中, 巧妙的是他们采用了一起分配的。最后前面的那个临时单链表。

二、相对转移,绝对转移

在学习汇编语言的时候,我们对不同的寻址方式肯定有很深的印象。但对于在汇编语言中同样重要的转移指令,只是一笔带过(用到了call 和 jxx ----------- 这里的 jxx 是指如 jmp jae jbe 这样的有条件转移指令和无条件转移指令)。然而,假如讲到动态链接库的链接实现则一定要提到这一内容。

所谓相对转移,就是这个二进制代码的中的他是能够在重定位的环境中不经修改,就能够运行的。如下面的情况,

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

Google