Intel平台下linux中ELF文件动态链接的加载、解析及实例分析(二): 函数解析与卸载

相信读者已经看过了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 这样的有条件转移指令和无条件转移指令)。然而,如果讲到动态链接库的链接实现则一定要提到这一内容。

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


719:	e9 e2 fe ff ff       	jmp    600 <loop_put_buffer>

变成一般的地址是这样的


   movl %eip,%eax
   addl $0xfffffee2,%eax
   movl %eax,%eip

这里旁边的 719 就是这个 ELF 文件与起始地址相比的偏移量,而在里面的 e9 e2 fe ff ff 如果写成看的往后退 0x11e 因为这是 ff ff fe e2(intel 是 little endian 表示方法)所表示的 -0x11e 的数。如果把 719 加上 5 再减去 600 就是这个数了。这便是处理器的相对转移。

还有另一种转移方式,就是绝对转移。


2b6:	ff d0                	call   *%eax

这个如果用最简单的代码来表示是


addl $2,%eip
pushl %eip
movl (%eax),%eip

很明显&

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值