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

Intel平台下Linux中 ELF文档动态链接的加载、解析及实例分析(一): 加载

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

这里所作的和上面的相类似,根据在前面从PT_LOAD program header 得到的文档映射的操作属性进行修改,但在zeroend>zerorpage的时候不同,把他映射成为进程独享的数据空间。这也就是一般的初始化数据区BSS的地方。因为zeroend是在文档中的映射的页面对齐尾地址,而zeropage是文档中的内容映射的页面对齐尾地址,这其中的差就是为未初始化数据准备的,这在1593-1597行之间体现,要把他的属性改成可写的,且全为0。


_dl_open() >> dl_open_worker() >> _dl_map_object() >> _dl_map_from_fd()

1606 if (l->l_phdr == NULL)
1607 {
……..
1611 ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (header->e_phnum
1612 * sizeof (ElfW(Phdr)));
1613
1614 l->l_phdr = memcpy (newp, phdr,
1615 (header->e_phnum * sizeof (ElfW(Phdr))));
1616 l->l_phdr_allocated = 1;
1617 }
1618 else
1619 /* Adjust the PT_PHDR value by the runtime load address. */
1620 (ElfW(Addr)) l->l_phdr = l->l_addr;

把phdr 就是program header 也纳入struct link_map的管理之中,一般的情况是不会有的,所以要copy过来。


_dl_open() >> dl_open_worker() >> _dl_map_object() >> _dl_map_from_fd()

1625 elf_get_dynamic_info (l);

这里调用的函数elf_get_dynamic_info是在加载过程中最重要的一个之一,因为在这之后的几乎任何的对动态链接管理的内容都要用要和这里的l_info数据组相关。


_dl_open() >> dl_open_worker() >> _dl_map_object() >> _dl_map_from_fd() >> elf_get_dynamic_info()

2826 static inline void __attribute__ ((unused, always_inline))
2827 elf_get_dynamic_info (struct link_map *l)
2828 {
2829 ElfW(Dyn) *dyn = l->l_ld;
2830 ElfW(Dyn) **info;
2831
2832
2833 info = l->l_info;
2834
2835 while (dyn->d_tag != DT_NULL)
2836 {
2837 if (dyn->d_tag < DT_NUM)
2838 info[dyn->d_tag] = dyn;
……………
2853 dyn;
2854 }
………….
2858 if (l->l_addr != 0)
2859 {
2860 ElfW(Addr) l_addr = l->l_addr;
2861
2862 if (info[DT_HASH] != NULL)
2863 info[DT_HASH]->d_un.d_ptr = l_addr;
2864 if (info[DT_PLTGOT] != NULL)
2865 info[DT_PLTGOT]->d_un.d_ptr = l_addr;
2866 if (info[DT_STRTAB] != NULL)
2867 info[DT_STRTAB]->d_un.d_ptr = l_addr;
2868 if (info[DT_SYMTAB] != NULL)
2869 info[DT_SYMTAB]->d_un.d_ptr = l_addr;
……………….
2874
…………
2876 if (info[DT_REL] != NULL)
2877 info[DT_REL]->d_un.d_ptr = l_addr;
………….
2879
2880 if (info[DT_JMPREL] != NULL)
2881 info[DT_JMPREL]->d_un.d_ptr = l_addr;
2882 if (info[VERSYMIDX (DT_VERSYM)] != NULL)
2883 info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr = l_addr;
2884 }
………….
2889 }

上面的__attribute__ 中的unused 是为了消除编译器在-Wall 情况下对于其中可能没有用到在函数中的局部变量发出警告,而alwayse_inline,很好解释,就是内联函数的强制标志。

2829行的l->l_ld是在前面的__dl_map_object_from_fd中的1455被给定的。也就是任何关于动态链接节的所在地址(参看附录B中的解释)。

很明显在2835至2854行之间的循环就是把l_info的内容都填充好。这为之后有很大的作用,因为这些节是能够找到如函数名和定位信息的,这里的的妙处是把数组的偏移量和d_tag相关联,代码简洁。

2856至2885便是对动态链接库的调整过程(这里调整的每一个节都是和函数解析有重要关系的,周详内容可参看附录A),假如我们考虑的更远一点,在前面的函数中的1521行一开始把整个文档连续的映射入内存,在这里就很好的得到解释,假如不是连续的,就没有办法在这里作一个统一的调整了。


_dl_open() >> dl_open_worker() >> _dl_map_object() >> _dl_map_from_fd()
1662 /* Finally the file information. */
1663 l->l_dev = st.st_dev;
1664 l->l_ino = st.st_ino;
1667 return l;
1670 }

最后就是把设备号和节点号加入就完成了最后的dl_map_object就行了,回头看1414行中对已加载的文档的搜索,就能够明白这里的作用了。

再回到dl_open_worker中


_dl_open() >> dl_open_worker()
2550 /* It was already open. */
2551 if (new->l_searchlist.r_list != NULL)
2552 {
…….
2556 if ((mode & RTLD_GLOBAL) && new->l_global == 0)
2557 (void) add_to_global (new);
2558
2559 /* Increment just the reference counter of the object. */
2560 new->l_opencount;
2561
2562 return;
2563 }

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