0. 写在前面

So库,又动态名库,是Linux下最常见的文件之一,也是Android中最常见的文件之一,是一种ELF文件。这种so库是程序运行时,才会将这些需要的代码拷贝到对应的内存中。但程序运行时,这些地址早已经确定,那程序引用so库中的这些代码地址如何确定呢,这就是这次要整理学习的内容,即so库的在链接和执行时的加载过程。

  1. 静动态库

在聊so库之前先聊聊静态库。为了程序更加优雅和高效,每一个程序的完成都是采用分而治之的方法,即同一个程序或者项目每个程序员都会完成不同的功能,有的功能是可复用的,而对于一些公共的可复用的功能,会使用库的形式来完成。比如我们在不同模块中多次用到了一个方法ar_public(),我们就可以将其包装到一个公共的文件里面,这样就如果其他的地方有调用就可以把引用这个公共文件从而调用这个方法,这样就有了静态库。简单来说静态库是链接的时候将库中所用到的程序拷贝进来,这样即使在执行阶段吧对应的库删掉都没有关系,因为此时对应方法的真实内容已经被拷贝过来了。但是虽然能做到方法共用,但带来最大的一个问题是如果是同一个项目的不同模块使用的话,使用的每个模块都会把这些拷贝过来,这样会使得应用的内存增大。也恰恰是由于会将静态库的这些方法拷贝进来如果静态库发生改变的话那程序需要重新编译。

为了解决上面的问题,于是又有了一个动态库的概念。动态库,又称共享库链接的时候它只包含需要的函数引用表,只有在执行的时候那些需要的函数才能被拷贝到内存中,而且在操作系统使用的是虚拟内存,使得一份共享库驻留在内存中被多个程序使用,也同时节约了内存。

   2. 位置无关(PIC

大家都知道,可执行文件在执行期的时候内存地址已经都确定了,而上面说的只有执行时才会确定那些函数地址拷贝到内存中,那基于这个特点大家第一想道的实现就是像那些段一样预留一个空间,但是这样做的一个最大问题就是会造成空间浪费,我们可以readelf去看下so库中的地址情况,从图一来看和data相关的地址都不是绝对地址(由于程序的起始加载地址都是从data开始,所以data相关的头如果不是绝对地址则可以认为加载的地址不固定)。

图一

在动态共享库中,如果库里面的代码发生改变,重新加载进来之后,我们必须保证它放到修改前的位置  ,否则我们还要为它找一个新的位置。而我们对于这个修改之后希望将动态库编译成可以在任意位置加载无需linker进行修改,这个叫做位置无关代码即PIC,也就是生成so库的-fPIC的那个PIC(这个指令就表示生成位置无关代码)。那PIC的原理是如何实现的呢,我们都知道数据段和代码段的距离在运行时是可以确定的,其中就是利用这一点来做地址定位的。如图二其实就是静态库中的一个变量,他的地址就是%rip+rel

图二

 3.静态分析:

这个代码无关的特性具体是怎么实现的呢,我们先从静态的角度来分析下这些是怎么执行的。自己写一个引用一个最常见的printf函数(如图三),编译之后通过最常用的objdump –d 反编译,先看下print_banner()对应的反编译代码(如图四)

图三

图四

从main函数开始,跳转到print_baner,而print_baner里面最主要的方法是callq400400这个pc值,我们再看下601018(rip+200C12)内存的内容。

图五

使用GDB的看下对应的值是多少,发现这个值是0x400406<printf@plt+6>,即执行后面pushq $0x0,然后再jmpq到<printf@plt-0x10>,即pushq到,然后再jmp到,然后再退出,这样整个printf的方法就执行完了。从静态代码来看只是几次jmp和push就完成了这个在so库中调用printf的操作,的确是这样,不过是这些jmp到的方法有自己的规范和名称,这就是GOT和PLT。

图六

  1. GOTPLT

首先我们说过这些是一个规范的有名称的,那么每一个可执行文件只要有这种so库的调用就一定会为他分配特定的存储空间。我们使用readelf看下(图7)

                              图7

关于GOT(),也叫全局偏移表,由于这个表和静态变量或者静态函数的相对地址是固定的,所以这个表的作用一个很大的作用是用来寻址。在上图中要注意的是.got的权限,是具有写权限的,也就是说这个在后面是会修改里面的值的,这个大家可以在对应的/proc下面去看下,这个地址是在data区的,关于这个是如何的写我们后面再看。

在反汇编代码中有一个pushq $0x0的操作,这个实际上是将printf对应的GOT数组中的条目方法入栈,且printf的条目偏移地址为0x0,对应GOT条目是一个共享库符号值保留的,而这里的0x0实际上是push第四个GOT条目,即GOT[3],下面是出自计算机系统圣经的CSAPP中GOT表的截图(图中的printf就是和本文so库中的printf条目一样)。

图8

第一个条目是指.dynamic段;第二个条目是指存放link_map结构的地址,动态链接器利用该地址来对符号进行解析,第三个条目是存放了指向动态链接器_dl_runtime_resolve()函数的地址,该函数用来解析共享库函数的实际符号地址,第四个条目就是printf的PLT[1]地址,也就是<printf@plt>的地址。

下面说下PLT,在图五的反汇编中可以看到有很多的带plt的方法,这些都是plt表中对应的条目。在图五中可以看到首先进到的是<printf@plt>地址,这些汇编很简单,前面也说过这里的pushq 0x0是将GOT[3]入队,执行完<printf@plt>之后,执行的jmpq到<printf@plt-0x10>中,这里也很指令简单,只不过操作数比较复杂,先说下pushq 0x601008,这里地址就是前面说的GOT[1],即这个程序的link_map,下一条jmpq 0x601010,则是GOT[2],即_dl_runtime_resolve()函数的地址。后续控制权就交给动态链接器了,解析出printf的地址。

对printf的解析完成之后,后面所有的对PLT条目中printf的调用都会直接跳转到printf中,而不是重新再进行这些跳转。通过watch 第一次jmp的值就可以看到,执行完成之后值以及由0x400406变化到0xFFFFFFFFF7A62800。

图9

东一句西一句啰嗦了这么多,其实总结起来就是对so库的里面方法的调用:

  1. 调用函数先跑到被调用的so库中方法的PLT(printf@plt)方法里面;
  2. PLT代码做一次到GOT中地址的间接跳转;
  3. GOT条目存放了指向PLT的地址,该地址存放在push指令中;
  4. push $0x0指令将printf() GOT条目的偏移量压栈;
  5. 最后的printf() PLT指令是指向PLT-0代码的jmp指令;
  6. PLT-0的第一条指令将GOT[1]的地址压栈,GOT[1]中存放了指向printf()的link_map结构的偏移地址;
  7. PLT-0的第二条指令会跳转到GOT[2]存放的地址,该地址指向动态链接器的_dl_runtime_resolve函数,_dl_runtime_resolve函数会通过把printf()函数的符号值加到.got.plt节对应的GOT条目中,来处理重定位。
  8. 下一次再做跳转的时候PLT条目会直接跳转到函数本身

在这里补充几点:1.为何引用so的方法需要先通过PLT再到GOT,这里这样做是因为这个data段的权限能读能写,而text段只有写的权限;2.这写的是so库方法的加载过程,而如果是仅是变量的话是由/lib/ld-linux.so.2填充的;3.关于_dl_runtime_resolve方法也可以去网上找下源码和实现,还有一些关于重定位相关的内容,等下次再总结分析吧,这个发生在so库之前,还有就是有的时候可以利用GOT的写权限做一些劫持的工作。

https://www.cnblogs.com/cdcode/p/5551649.html

https://blog.csdn.net/ylcangel/article/details/18145155

https://www.jianshu.com/p/eca50b89a423

https://docs.oracle.com/cd/E24847_01/html/E22196/chapter6-14428.html

https://www.cnblogs.com/fellow1988/p/6158240.html

https://blog.csdn.net/linyt/article/details/51635768

https://blog.csdn.net/conansonic/article/details/54634142

https://www.cnblogs.com/xingyun/archive/2011/12/10/2283149.html

https://www.freebuf.com/articles/system/135685.html

https://bbs.pediy.com/thread-221821.htm

https://www.cnblogs.com/pannengzhi/p/2018-04-09-about-got-plt.html

https://www.cnblogs.com/LittleHann/p/4244863.html

so库方法的调用过程相关推荐

  1. Collection 属性ArrayList.add方法内部调用过程

    Collection 属性 //二进制搜索阈值 private static final int BINARYSEARCH_THRESHOLD = 5000; //改变阈值 private stati ...

  2. 递归调用方法时栈内存是如何变化的?(使用内存图演示递归调用过程)

    文章目录 什么是栈内存 演示方法递归调用过程 什么是栈内存 在学习递归实现原理之前,我们先了解一下栈内存. 栈内存是计算机中的一种数据存储方式,是 Java 进程启动时候在内存中开辟的存储空间. 栈内 ...

  3. 启动go服务_go微服务框架go-micro深度学习 rpc方法调用过程详解

    摘要: 上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取serv ...

  4. JAVA方法调用过程(最详细的解释)

    弄清楚方法的调用过程,对于我们学习java极为重要,下面是调用过程的详细描述: 1.编译器查看对象的声明类型和方法名. 编译器去寻找所有名字为f但参数类型不同的方法.例如可能存在方法f(int)和方法 ...

  5. java 获取当前方法的调用栈

    本文的出发点在于处理现场问题时,想看到方法的调用过程 StackTrace(堆栈轨迹)存放的就是方法调用栈的信息,每次调用一个方法会产生一个方法栈,当前方法调用另外一个方法时会使用栈将当前方法的现场信 ...

  6. 记录一次C语言调用go生成的动态库的踩坑过程

    记录一次C语言调用go生成的动态库的踩坑过程 问题现象 由于某些特殊原因,需要在C语言中调用go语言生成的so,本来挺顺利,一切都运行的很好.突然某一天,不知道怎么回事,再一个新程序中无法正常运行了, ...

  7. Java05-day05【方法(概述、调用过程图解)、带参方法、带返回值方法、重载、方法参数传递(基本类型、引用类型)】

    java零基础入门到精通(2019版)[黑马程序员] 视频+资料:[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg   提取码:zjxs] &qu ...

  8. Spring的refresh()方法调用过程

    Spring的refresh()方法调用过程 refresh()是Spring中比较核心的方法,Spring所有的初始化都在这个方法中完成 具体代码如下 public void refresh() t ...

  9. java 调用对象的方法_JAVA调用对象方法的执行过程

    JAVA调用对象方法的执行过程: ①.编译器查看对象的声明类型和方法名.假设调用x.f(parameter),  且隐式参数x声明为C类型的对象,有可能在C对象中存在多个参数类型和参数个数不同的f的方 ...

  10. mybatis源码分析(方法调用过程)

    十一月月底,宿舍楼失火啦,搞得20多天没有网,目测直到放假也不会来了... 正题 嗯~,其实阅读源码不是为了应付面试,更重要的让你知道,大师是怎样去写代码的,同样是用Java,为啥Clinton Be ...

最新文章

  1. Windows安装用于OCR的Tesseract及使用命令行参数进行OCR
  2. catboost是什么?相对于xgboost以及lightgbm有什么优势?如何使用randomSearchCV和Catboost进行组合获取最优参数组合?
  3. java获取屏幕图像_Java捕获当前屏幕图像
  4. JAVA基础——编程练习(二)
  5. 【招聘(北京)】北京华光普泰生物招聘.NET软件开发
  6. 绿茶软件测试自学,7号心理测试小程序
  7. Windows 10 Build 9879 新变化(内含ISO下载)
  8. 多目标跟踪——MOT数据集的学习笔记
  9. SSLRobot:适用于HttpWatch的免费SSL / TLS测试工具
  10. c语言程序构建,c语言开发环境构建及简单的c程序设计.doc
  11. Drools 文档(目录)
  12. linux 0.11根文件系统,构建一个最小Linux根文件系统
  13. javascript简单介绍总结(二)
  14. 提取Redis事件机制源码为我所用
  15. 恋恋山城 Jean de Florette (1986) 男人的野心 / 弗洛莱特的若望 / 让·德·弗罗莱特 / 水源 下一部 甘泉,玛侬...
  16. Jpa配置实体类创建时间更新时间自动赋值,@CreateDate,@LastModifiedDate
  17. IAR使用方法建立工程文件超详细操作步骤
  18. 千年古刹南普陀寺义工十年发展已逾6000人
  19. 2012年终总结之pcode概述
  20. 使用lgb.cv时出现ValueError: Supported target types are: (‘binary‘, ‘multiclass‘). Got ‘continuous‘ instea

热门文章

  1. 京东获取商品历史价格信息 API 返回值说明
  2. 红米note3android驱动,红米Note3手机驱动
  3. 推荐系统实践(三)ICF和UCF
  4. (二)Jointly Optimizing Diversity and Relevance in Neural Response Generation
  5. qml 中英文虚拟键盘
  6. 频域处理:傅里叶变换及小波变换
  7. 产品经理知识体系专题
  8. HTML+CSS练习——实现京东登录静态页面
  9. 远程访问双层嵌套Openstack云下的Windows虚机(by quqi99)
  10. 施努卡:机器视觉尺寸检测(机器视觉表面缺陷检测)