出处:http://my.oschina.net/wolfcs/blog/144412#OSC_h2_1

android下运行时动态链接dlopen()和dlsym()的实现

在android中,就如同在Linux下一样,我们也可以在app中,运行时动态加载一些动态链接库,执行调用其中的函数等操作。实现这一切最终依靠的就是dlopen()等几个函数。关于这几个函数的原型机这些API的用法,可以参考这一篇。而此处我们就来看一下,在android

c标准库的bionic中,这些函数究竟是如何实现的。

dlopen()函数

首先是dlopen()函数。我们给这个函数传递一个动态连接库的文件名或路径,及一些flag,这个函数返回给我们一个动态链接库的句柄,以方便我们后续执行查找symbol等操作。这个函数的代码在android

codebase中的路径为bionic/linker/dlfcn.c。我们来看一下它的实现:

可以看到,这个函数本身的结构都还蛮清晰的。它主要完成几个事情:

1.

获取到一个互斥量。这个互斥量用于保护动态连接库的链表。

2.

根据动态连接库的路径名或文件名,查找一个动态链接库。这个过程实际上可能会返回一个已经加载了的动态连接库的句柄,但也可能会去新加载一个动态连接库,并返回其句柄。

3.

如果上一步查找动态连接库的操作成功完成,即找到了一个已经加载的动态连接库或者是成功的新加载了一个动态连接库。则它会去执行该动态连接库的初始化部分代码,并增加动态连接库的引用计数。而如果上一步的查找没有成功,则此处会设置出错信息,以便于user可以通过dlerror()了解到执行出错的情况。

4.

最后就是释放互斥量,并将结果返回给调用者。

此处我们可以看一下,那个所谓的动态连接库的句柄究竟是个什么东西。可以看到,它是一个soinfo的对象。

接下来我们先偏个楼,来看一下linker提供给user用的了解error信息的接口。

dlerror()和set_dlerror()

当open一个动态链接库出错时,dl族函数的调用者可以通过dlerror()函数来对出错的原因有更多的了解,这个函数返回一个关于出错状况的error

message。在linker

open一个动态连接库出错时,它会自动的设置相关的error message。我们来看一下dlerror()函数和set_dlerror()函数的实现。

首先是dlerror()的code:

可以看到这个函数的实现非常简单,就只是返回一条error message

dl_err_str而已。但这个error

message究竟是如何设置的呢?它当然是set_dlerror()函数设置的:

set_dlerror()这个函数所完成的事情就是向一个缓冲区中格式化输出一条error

message,并设置dl_err_str。可以看到这条error

message将主要由两部分组成,一部分来自于dl_errors数组,set_dlerror()函数会依据错误类型来选择数组的成员,从而这个部分将是关于出错的类型的信息。另一部分则来自于linker_get_error()函数。接下来我们就来看一下linker_get_error()函数到底返回了一个什么鸟东西:

这个函数倒也简单,返回了一个字串而已__linker_dl_err_buf。那这个__linker_dl_err_buf又是从哪儿来的呢?可以看上面的那个宏DL_ERR,它正来自于这个宏。在加载动态链接库过程出错的那个点上,宏DL_ERR会被调用,由这个宏的实现,我们可以看到,__linker_dl_err_buf将会包含出错点的函数名,文件名,行号等信息,即error

message的这个部分,主要给调用者提供一些关于出错点的详细的信息。

查找一个动态链接库

回归主题,来看linker到底是如何查找一个动态链接库的。首先来看一下find_library()函数是怎么实现的:

先不去关心那些错误处理的部分,即假设传入的动态连接库文件名为一个有效的文件名,而各个函数的返回值在预期范围内的情况。这个函数的执行流程大致为:

1.

在已经加载的动态连接库链表里面找一个动态连接库。如果找到了,并且没有错误情况发生,则将找到的动态连接库返回给调用者。否则,执行下面的第2个步骤。

2.

此时,即意味着用户请求的动态连接库还没有被加载。则此处会调用load_library()来加载一个动态连接库,调用init_library()函数来完成对于动态连接库句柄的初始化,并将结果返回给调用者。

此处还有一点值得我们关注的就是,当传入的name参数为空的情况,可以看到在bionic中,是会返回somain动态连接库的句柄,根据code中,对于这个对象的注释,该对象指向main

process,而并不是一个全局的符号表:

接下来我们来看一下,linker到底是如何在一加载动态连接库中来查找一个动态连接库的。来看find_loaded_library()函数的实现:

由此我们也可以非常准确的了解到,在系统中,linker是用一个链表来组织各个动态连接库句柄的。并且,可以看到,linker就只是依据于动态连接库文件的文件名来查找一个动态链接库而已,尽管由注释看起来写这个code的人也觉得这样不是很合理。

接下来我们来看一下,新加载一个动态连接库,即创建动态连接库句柄的更详细的执行过程,来看load_library()函数的实现:

基本上即是依据于ELF文件格式,来load一个动态连接库。更详细信息,此处先不做过多讨论。

dlsym()函数

然后来看一下dlsym()函数,通过一个动态连接库句柄,来查找一个symbol的过程。dlsym()函数的实现如下:

整体而言,这个函数做了几件事情:

1.

获取到一个互斥量。这个互斥量用于保护动态连接库的链表。

2.

在指定的动态连接库上,为特定的符号查找一个Elf32_Sym对象,及包含此Elf32_Sym对象的动态连接库的句柄。

3.

通过获取到的动态连接库的句柄,即soinfo对象,及Elf32_Sym对象及算出符号的地址。

4.

释放互斥量。并将计算出来的符号的地址返回给调用者。

接下来我们看一下,为特定的符号查找一个Elf32_Sym对象和包含此Elf32_Sym对象的动态连接库句柄及计算符号地址的过程。

为一个特定的符号查找Elf32_Sym对象

首先我们先来看一下为特定的符号查找一个Elf32_Sym对象及包含此Elf32_Sym对象的动态连接库句柄的过程。由前面dlsym()函数的code,可以看到这个过程是依据于handle的类型而分为3种情况来执行的,即handle为RTLD_DEFAULT,RTLD_NEXT及其他。

首先是handle为RTLD_DEFAULT的情况,可以看到它就仅仅是简单调用了lookup()函数来完成这整个搜索过程而已。我们来看一下lookup()函数的定义:

我们知道,在handle为RTLD_DEFAULT的情况中,调用lookup()时,其start

soinfo是一个NULL值。由上面的code来看,则意味着,在这种情况下,linker将遍历系统中已经加载的所有动态链接库,即系统的soinfo对象链表,并通过调soinfo_elf_lookup()函数来在每一个动态连接库里查找那个符号(关于soinfo_elf_lookup()函数更详细的信息,会在后面讨论)。

也就是说,当我们想要从全局符号表中查找一个符号时,我们给dlsym()函数传递的handle参数应该为RTLD_DEFAULT,而不是用NULL

filename来调用dlopen()函数而得到的那个返回的handle。

接下来,我们来看handle为RTLD_NEXT的情况。不过那个__builtin_return_address(0)到底是一个什么东西呢?它其实是一个gcc的内置函数,用于帮助获取给定函数的调用地址,此处即是要获取dlsym()函数的调用地址。__builtin_return_address()接收一个称为level的参数。这个参数定义希望获取返回地址的调用堆栈级别。例如,如果指定level为0,那么就是请求当前函数的返回地址。如果指定level为1,那么就是请求调用了当前函数的函数的返回地址,以此类推。(关于更多的信息,请参考IBM

developer works上的一份文档,Linux内核中的GCC特性)。

那获取到的dlsym()函数的返回地址又有什么用呢?接下来来看一下find_containing_library(ret_addr)函数又做了些什么事情:

可以看到,这个函数所完成的事情即是查找到dlsym()函数的调用者所属的那个动态链接库的句柄。接下来,handle为RTLD_NEXT的情况下查找特定的符号的一个Elf32_Sym对象及包含此Elf32_Sym对象的动态链接库句柄的过程即是,同样调用lookup()函数来在链表里搜索。即handle为RTLD_NEXT的情况下,会从dlsym()函数的调用者所属的动态链接库的下一个开始,在动态链接库链表中来查找特定的符号的一个Elf32_Sym对象及包含此Elf32_Sym对象的动态链接库句柄。

然后是第三种情况,即handle来自于一个dlopen()函数调用的返回值的情况,或者说其他情况。此种情况下,则会直接调用soinfo_lookup()函数来在一个特定动态连接库句柄上查找一个特定的符号。我们来看soinfo_lookup()函数的实现:

soinfo_lookup()函数本身则是直接调用soinfo_elf_lookup()函数来完成整个的查找的过程。而在soinfo_elf_lookup()函数中,则是依据于soinfo对象中存储Elf32_Sym对象的这种特殊的方式,来遍历查找。

计算符号的地址

dlsym()返回的符号的地址,是算出来的,而不是直接从什么地方读出来的。可以看到这种计算的方法,即symbol_address=(sym->st_value+found->base)。符号的地址即为动态链接库的基地址+该符号的Elf32_Sym对象的st_value值。

Done。

android 禁用dlsym_(转载)android下运行时动态链接dlopen()和dlsym()的实现相关推荐

  1. android下运行时动态链接dlopen()和dlsym()的实现

    在android中,就如同在Linux下一样,我们也可以在app中,运行时动态加载一些动态链接库,执行调用其中的函数等操作.实现这一切最终依靠的就是dlopen()等几个函数.关于这几个函数的原型机这 ...

  2. 【java】Java运行时动态生成类几种方式

    1.概述 转载:Java运行时动态生成类几种方式 这里发现自己不知道的,原来Java 还能自己编译自己,学到了. 最近一个项目中利用规则引擎,提供用户拖拽式的灵活定义规则.这就要求根据数据库数据动态生 ...

  3. Android运行时动态全屏以及旋转屏幕时不重新装载

    Android运行时动态全屏以及旋转屏幕时不重新装载 最近要做一视频播放器,在横屏时需要用到全屏播放,在网络上搜索"android 全屏",查到的文章全是如何设置全屏,通过them ...

  4. 运用delphiXE RTTI在运行时动态获取信息及获取某个TComponent类或TObject类的RttiType信息的案例

    运用delphiXE RTTI在运行时动态获取信息及获取某个TComponent类或TObject类的RttiType信息的案例 一.理解RTTI 先看看官方文档:http://docwiki.emb ...

  5. SAP UI5 应用开发教程之五十八 - 使用工厂方法在运行时动态创建不同类型的列表行项目控件试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  6. .NET6运行时动态更新限流阈值

    自FireflySoft.RateLimit发布以来,帮助了不少需要在.net中进行限流处理的用户.前段时间有个开发者发了一个pull request,大意是Redis重启的时候Lua script会 ...

  7. 运行时动态的开关 Spring Security

    为什么80%的码农都做不了架构师?>>>    1. 为什么要在运行时动态的开关 Spring Security? 考虑这样一个场景,当我们构建了一整套微服务架构的系统后,公司某个内 ...

  8. Unity项目运行时动态更新光照贴图 | LightMap

    Unity项目运行时动态更新烘培的光照贴图 动态更新烘培的光照贴图 场景的物件没有发生变化(也就是说没有运行时加载在场景上的Prefab) 场景的烘培贴图已经更新,但是有些物件prefab想运行时加载 ...

  9. LINQ to SQL 运行时动态构建查询条件

    原文地址:http://msdn.microsoft.com/zh-cn/dd567295.aspx 在进行数据查询时,经常碰到需要动态构建查询条件.使用LINQ实现这个需求可能会比以前拼接SQL语句 ...

最新文章

  1. 研究型AI面经 | 来自一位Reddit网友谷歌面试经验分享
  2. JSR303(Bean Validation 1.0)
  3. Spring autowire 自动装配简介
  4. 【重复制造精讲】1、入门介绍
  5. uniapp ios时间戳获取不到_2折甩卖、货架被抢空…青岛这家大超市要关门,开业不到两年!停业时间戳...
  6. live2dviewer android,live2dviewerex安卓版
  7. VPP命令行:启动配置,HTTP服务,DPDK配置
  8. @sql 单元测试_10个最常见SQL单元测试错误
  9. ubuntu 11.10 下network proxy 的设置问题
  10. 【java】之常用四大线程池用法以及ThreadPoolExecutor详解
  11. 微信公众号推送模板消息,推送个人消息,给指定的人发送模板消息
  12. oracle导出为dmp文件,oracle导出dmp文件的2种方法
  13. 计算机怎么格式化电脑吗,电脑怎么格式化
  14. 电信光猫F660 4台限制破解
  15. 正弦波振荡的常见电路
  16. python代码怎么修改_python修改微信和支付宝步数的示例代码
  17. 【luogu P3426】SZA-Template(字符串)(KMP)
  18. 客户订单管理系统使用教程
  19. jmeter源码解读
  20. ChatGPT 类大语言模型为什么会带来“神奇”的涌现能力?

热门文章

  1. 项目四 CentOS使用kubeadm部署工具部署测试环境的K8s集群---Kubectl命令使用以及安装dashboard界面
  2. 一种分布式深度学习编程新范式:Global Tensor
  3. Vue-数字滚动和翻牌器
  4. 利用IP冲突 攻击目标服务器
  5. 怎么利用MBR加密硬盘?
  6. 小米八显示无服务器,小米8突然没信号了
  7. 好消息与坏消息应该怎么说
  8. 策略验证_指标买点分析技法_运用BIAS乖离率指标选择买点
  9. LeetCode-整数反转【解决Integer的OverFlow】
  10. snoopy采集phpchina示例