动态链接器
动态链接器属于glibc的一部分, 本文章用glibc2.17分析
link map
一个可执行文件或一个共享库对应一个link map, link map有三种类型, lt_executable, lt_library和lt_loaded。 其中lt_executable代表可执行文件,lt_loaded代表dlopen的共享库, lt_library代表lt_executable或lt_loaded或lt_library的DT_NEEDED项的link map。(或是预加载的共享库?)
link map 是got.plt的第二项的值, 数据结构为 struct link_map。结构体中的成员含义如下:
成员 | 含义 |
---|---|
l_type | link map类型 |
l_ld | 指向dynamic section |
l_ldnum | dynamic section entity 数量 |
l_info | dynamic section 解析后的数据 |
l_direct_opencoun | dlopen次数 |
l_rpath_dirs | DT_RPATH数据 |
real | link map的地址 |
l_global | 对应RTLD_GLOBAL和RTLD_LOCAL,该动态库的符号是否可以用于后续的符号解析(PS:manpage 上写的是后续加载的动态库的符号解析, 我理解的是后续的符号解析,因为当先打开main program的link_map再加载该动态库, 用main program的link map 也可以解析到该动态库函数的地址) |
l_reserved | 获取新模块的link map时用于判断模块否加载过 |
l_loader | 装载该模块的模块,main program 和 dlopen的共享库都是NULL |
l_name | link_map 对应的模块的名字 main program对应的是"“,所以可以用dlopen(”")获取 main program的link map |
l_scope[0] | 等于loader的l_scope[0],如果没有loade,指向l_searchlist |
l_local_scope[0] | 指向本模块的l_searchlist |
l_scope_mem | l_scope指向的内存空间 |
l_symbolic_searchlist.r_list | |
l_libname | libname 共享库有4个名字, libname, realname,soname, linkname |
l_initfini | 用于调用构造析构函数 |
l_searchlist | 包括可执行文件模块和依赖的所有共享库的link map |
l_ns | namespace, 和loader共享一个namespace |
l_phdr | 程序头虚拟地址偏移 |
l_map_start | 模块映射的起始地址 |
l_map_end | 模块映射的终止地址, 通过PT_LOAD的segment算出来 |
l_contiguous | 模块映射各个segment之间是否有空洞 |
l_versions | .gnu.version_r section 数据 |
l_used | 是否有符号被引用 |
l_serial | 进程第几个加载该共享库 |
l_ino | 共享库inode |
l_dev |
特殊的link map
1 RTLD_DEFAULT 搜索 base namespace
2 RTLD_NEXT 按namespace的搜索顺序搜索, 略过调用的link map及前面的link map
3 vdso的link map 属于base namespace, 但是loader不是main program, 没有loader
关键函数
_dl_map_object 在已经加载的模块中寻找object, 如果找不到的话,获取共享库的realname并打开, 接着调用_dl_map_object_from_fd加载object, 创建link map
_dl_map_object_deps 调用_dl_map_object, 打开依赖的模块
_dl_map_object_from_fd 调用_dl_new_object创建link map并加载object
_dl_new_object 创建一个 link map, 初始化内容
open_aux 调用_dl_map_object, 通过原模块的link map类型确认打开的新模块的link map 类型
do_sym 根据handle类型确认
_dl_lookup_symbol_x
全局变量
GL(dl_ns)[] 0指向全局符号的模块链表的名字空间,其它指向dlmopen的名字空间
GL(dl_rtld_map) 动态链接器的link map
GL(dl_nns) 使用的名字空间的数量
GL(dl_load_adds) 装载的共享库的数量
GLRO(dl_lazy) 是否默认为lazy加载
常量
LM_ID_BASE main program 和它的依赖所在的nsid
__LM_ID_CALLER dlopen打开的link map的默认nsid,会重新赋值loader的nsid
宏
RTLD_NOW
RTLD_LAZY
在加载的时候还是引用的时候重定位RELA和JMPREL中的符号
RTLD_LOCAL
RTLD_GLOBAL
GLOBAL的话可以使用dlsym(RTLD_DEFAULT) dlsym(RTLD_NEXT) 和直接引用获取到该共享库定义的动态符号的地址, RTLD_LOCAL只能通过 dlsym(handle)获取到地址
重定位类型
R_X86_64_COPY 非fPIC的object引用shared object的变量