动态链接器

动态链接器

动态链接器属于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_typelink map类型
l_ld指向dynamic section
l_ldnumdynamic section entity 数量
l_infodynamic section 解析后的数据
l_direct_opencoundlopen次数
l_rpath_dirsDT_RPATH数据
reallink 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_namelink_map 对应的模块的名字 main program对应的是"“,所以可以用dlopen(”")获取 main program的link map
l_scope该命名空间的loader的searchlist。如果是dlopen的并且不是主模块依赖或者依赖的依赖的object, 还包括该object的searchlist, [0]为命名空间的loader的searchlist,[1]为该object的searchlist
l_local_scope[0]该object的searchlist
l_scope_meml_scope指向的内存空间
l_symbolic_searchlist.r_list
l_libnamelibname是一个链表, 包括realname,soname. realname包含路径, soname可以通过readelf -d查询.相关的还有linkname, 为lib*.so格式, 不包含版本信息, 用于静态链接.ldconfig可以生成一个名为soname的软链接, ldconfig -p的输出为soname => realname .realname可以是一个链接, linkname可以指向ld脚本
l_initfinimain program存放所有的依赖,其它存放直接依赖. main program会重排, 把依赖的放在被依赖的前面. 调用object的构造析构函数会使用它
l_init_called构造函数是否已经被调用过
l_searchlist包括可执行文件模块和依赖的所有共享库的link map, 指向l_initfini的后半段. 可执行文件在最前面, 广度优先搜索排序
l_nsnamespace, 和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_deps -> openaux -> _dl_map_object -> _dl_map_object_from_fd -> _dl_new_object

_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并用mmap 的MAP_PRIVATE 映射object的LOAD segment

_dl_new_object 创建一个 link map, 初始化内容

openaux 调用_dl_map_object, 通过原模块的link map类型确认打开的新模块的link map 类型


_dl_start -> _dl_start_final ->_dl_sysdep_start -> dl_main -> _dl_map_object_deps
-> _dl_relocate_object

_dl_start_user -> _dl_init
_dl_relocate_object 装载时解析符号, 用l_scope
_dl_runtime_resolve 运行时解析符号, 用l_scope. .got.plt第三项的值
dl_sym 手动解析符号, DT_DEFAULT时用本object的l_scope, DT_NEXT时用loader的l_local_scope, 用dlopen的handle时用handle(也是一个link_map)的l_local_scope

do_sym 根据handle类型确认

_dl_lookup_symbol_x

全局变量

//?GL(dl_ns)[0]._ns_main_searchlist指向全局符号的模块链表的scope,其它指向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
在加载的时候还是引用的时候解析未定义的函数动态符号

RTLD_LOCAL
RTLD_GLOBAL
解析动态符号的时候会搜索一系列的object.把这一系列的object称为scope.scope又分为global scope和local scope.全局scope包括lt_executable和它所有的依赖项或lt_loaded和它的所有依赖项.用local scope的时候包括object的所有依赖项.
dlopen的handle能查找全局符号和本object的符号
RTLD_GLOBAL会把该object加入lt_executable的全局scope.
RTLD_LOCAL 之后加载的object不能查找该object的符号.
RTLD_DEFAULT 查找调用模块的l_scope
RTLD_NEXT 查找调用模块的起始loader的l_local_scope(lt_executable
的l_local_scope和l_scope相同, 但是lt_loaded
的l_local_scope比l_scope少了lt_executabled的searchlist), 跳过调用模块和调用模块前所有模块
直接用handle 查找handle的l_local_scope

重定位类型

pic用got表来引用动态符号
R_X86_64_COPY 非fPIC的object引用shared object的变量
R_X86_64_GLOB_DAT 导入的变量值, 放在.got中
R_X86_64_JUMP_SLO 导入的函数地址, 放在.got.plt中, 用plt调用
R_X86_64_64 object起始地址的64位偏移量?
R_X86_64_IRELATIV ifunc使用, ifunc使用got表
R_X86_64_RELATIVE 模块基址重定位

.plt 间接跳转的地址存在.got.plt中的间接跳转代码
.plt.got 间接跳转地址存在.got中的间接跳转代码
.got.plt 存放外部函数符号地址的表, 地址可以在运行时解析
.got 存放外部符号地址的表, 地址可以是变量地址, 也可以是函数地址, 地址在加载时解析(如果一个函数的地址被直接通过got读取, 那么该函数的地址放在.got而不是.got.plt中, 因为如果读取的时候该符号还没被解析的话, 就会读取一个错误的地址, 所以要在加载时解析, 同时, 如果调用该函数的话会通过.plt.got中的项调用)

如果符号在编译单元中或者静态库中定义,那么静态链接, 编译后就有地址。如果符号在动态库中定义, 动态链接,放在动态符号表中, 符号在库加载时或者执行时用plt和got解析地址。
一般情况下, 没有定义的符号链接时发生错误。 但是没有定义的弱符号不会链接错误,(包括没在编译单元中定义和没在静态库和动态库中定义),但是执行时调用会发生段错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值