手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>网站运营>建站经验>列表

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

来源:互联网 作者:west263.com 时间:2008-04-16
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!

2104

2105 ");

从这里定义的名称ELF_MACHINE_RUNTIME_TRAMPOLINE,我们就能够看出这个函数不简单(TRAMPOLINE在英语中是蹦床的意思,就是要make your brain curving的那种怪怪的东西),后面的代码也确实说明了这一点。

在前面的.text是下面的代码是可执行,.globl _dl_runtime_resolve是表明这个函数是全局性的,假如没有这一项,那我们前面看的got[2]=& _dl_runtime_resolve就不能编译通过-----编译器可能很难找到他的定义。.type _dl_runtime_resolve, @function是函数说明。 .align 16处便是16字节对齐。

我们知道在前面的调用函数过程中已压入了两个参数(第一个是动态链接库的struct link_map* 指针,另一个是rel的索引值)这里先保存以前的寄存器值,而到这个时候16(%esp)就是第二个参数,12(%esp)第一个参数,这里作的原因是下面的fixup的函数以寄存器传递参数。

我先不管fixup具体内容是什么,单就看他结束的内容就很能说明代码作者的优秀。先pop两个寄存器的值,而又xchg 陎,(%esp)和栈顶的内容,这有两个目的,一是恢复了eax的值,另一个作用是栈顶是函数返回的地址,而fixup返回的eax就是我们想找的函数有内存中的地址。这就自然跳到那个地方去了。但假如您认为这就好了,那也错了,因为您不要忘记我们之前还压入了两个参数在栈中。所以用了ret ,这在intel的指令中表示


popl %eip
add x8,%esp


的组合。(很出色!!!!!!!)

您还能够参看《程式的链接和装入及Linux下动态链接的实现》 网址为 http://www-900.ibm.com/developerWorks/cn/linux/l-dynlink/index.shtml 里面的有一幅图正好说明此的ELF_MACHINE_RUNTIME_TRAMPOLINE。

那直接看fixup函数的内容


124 Elf32_Addr fixup(struct link_map* lmap,Elf32_Word reloc_offset)
125 {
126 Elf32_Sym* symtab=lmap->l_info[DT_SYMTAB].d_un.d_ptr;
127 char* strtab=lmap->l_info[DT_STRTAB].d_un.d_ptr;
128 Elf32_Rel* reloc = (Elf32_Rel*) (lmap->l_info[DT_JMPREL].d_un.d_ptr reloc_offset);
129 Elf32_Sym* sym=&symtab[Elf32_R_SYM(reloc->r_info)];
130 char* symname=sym->st_name strtab;
131 Elf32_Addr reloc_addr=lmap->l_addr reloc->r_offset;
132
133 Elf32_Addr symaddr=0;
134
135
136
137 symaddr=do_lookup(lmap,symname);
138
139
140
141 if (symaddr>0)
142 {
143 *reloc_addr=symaddr;
144 return symaddr;
145 }
146
147 exit(-2);
148
149
150
151
152
153 }

这里是给出了从一个动态链接库中可重定向的reloc_offset得到要解析函数的名称,假如用图示的方式表示就如下图:

您可能会想:其实还能够用另一种方法,就是把这个reloc sym的st_value直接写入前面的这个调用重定向函数相对应的got中。这样解析时的速度会更快。但现实这样却可能对整个ELF文档结构体系带来很大的麻烦。我将对每一点说明:

  1. 假如是这个reloc sym的地址,那对于一个动态链接库而言,他的加载地址本身就是动态确定的。
  2. 假如用的是那个Elf32_Sym的st_value地址,那倒是能够和lmap->l_i nfo[DT_STRTAB]一起得到这个sym的name,但假如考虑到在编译的时候有些函数是只对本模块有效,可见的,如在一个文档中定义为 static的函数,则他就是局部可见的,那个时候就不可能是解析为这个函数,而且对c 函数更有更为复杂的情况,这样就会需要一个字段来表示他的属性,这就是要有了st_info这个数据成员变量。这也就要有了sym的参和了。
  3. 光有Elf32_Sym还是不行,因为就重定位而言他本身更有一点信息,就是这一个relocation symbol是在本地解析,还是在另外一个真正意义上的动态链接库内被解析,这一情况主要是发生在几个文档编写的模块中,他们编写的一些函数就在链接的时候被确定了,而另一些则没有,区分的就是relocation 中的r_info了。

从上面的分析来看,一种规范的设计有许多的考虑因素,假如只单一的考虑,那是不行的,特别是要对多个操作系统和平台统一的规范,不能因为就是考虑效率一条就能够了。

在143行是对前面要重定位的函数实现真正的解析函数到位,这样在这个函数被再次调用的时候就不用再来一次了,本来这时就对这个 relocation symbol r_info的判断,现在都已略去了。

真正的解析在do_lookup中实现了,我这里还是他的实现伪代码:


90 Elf32_Addr do_lookup(struct link_map* lmap,char* symname)
91 {
92 struct link_map* search_lmap=NULL;
93 Elf32_Sym* symtab;
94 Elf32_Sym* sym;
95 char* strtab;
96 char* find_name;
97 int symindx;
98
99 Elf32_Word hash=elf_hash_name(symname);
100 for_each_search_lmap_in_search_list(lmap,search_lmap)
101 {
102 symtab=search_lmap->l_info[DT_SYMTAB].d_un.d_ptr;
103 strtab=search_lmap->l_info[DT_STRTAB].d_un.d_ptr;
104 for (symindx=search_lmap->l_buckets[hash % search_lmap->l_nbuckets];
105 symindx!=0;symindx=search_lmap->l_chain[symindx])
106 {
107 sym=&symtab[symindx];
108
109 find_name=strtab sym->st_name;
110 if (strcmp(find_name,symname)==0)
111 return sym->st_value search_lmap->l_addr;
112 }
113
114 return 0;
115
116
117
118 }
119 }

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