研究一下dlopen的源码。
通过调用链的形式分析dlopen源码。
首先,函数的入口是dlopen (const char *file, int mode)
1 | void * |
函数跳转到了__dlopen
,这个函数实际上做了两件事情,将struct dlopen_args args
赋值,然后运行dlopen_doit
。
1 | void * |
其中结构体struct dlopen_args
是这样的:
1 | struct dlopen_args |
然后dlopen_doit
的函数则是调用了__dl_open
函数。
1 |
|
对于__dl_open
函数,实际上调用了dl_open_worker
函数。
1 | void * |
其中关键的数据存储在struct dl_open_args
内,它的结构如下所示
1 |
|
在dl_open_worker
函数中,最重要的加载数据一步是通过调用_dl_map_object
实现的。
1 | /* Load the named object. */ |
接下来就是_dl_map_object
函数,其主要的内容如下:
1 | struct link_map * |
其中文件是通过open_path
调用的。
1 | static int |
其中open_verify
函数通过调用__libc_read
对ELF文件的文件头实现了读写。
接下来是_dl_new_object
函数,它主要初始化一个新的struct link_map*
数据结构,并把它加入到loader
对应的单链中(猜测)。
最重要的是函数_dl_map_object_from_fd
。这个函数包括了
- 对
link_map
的二次查找,是否已经存在映射了.so文件的 - 提取ELF头文件信息(程序入口地址,头文件信息等)
- 对phdr(程序头表进行遍历),根据每个程序头的type类型做具体的操作
- 进行映射
1 | maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart; |
- 调用
mprotect
修改内存权限 - 修改TLS等设置
- 修改符号表
1 | /* Set up the symbol hash table. */ |
最后,是_dl_map_segments
函数
1 | /* This is a position-independent shared object. We can let the |