对前面add.c稍作修改,
#include <stdio.h>
int add_count = 0;
extern int sum_count;
extern void print_log(const char *ident, int line);
int add2(int num1, int num2)
{
int result= 0;
result = num1 + num2;
add_count ++;
return result;
}
int sum(int num1, int num2, int num3)
{
sum_count++;
int sum = add2(num1, num2);
return add2(sum, num3);
}
编译成so gcc -shared -fPIC add.c -o libadd.so。查看ELF program header.
和可执行文件类似,只是LOAD的virtAddr起始地址是0x0.说明so在load到内存中时地址是不确定的。
当系统在装载某个包含so的进程时,也需要装载so。装载器会更具当前地址空间的空闲情况,动态分配一块足够大小的虚拟地址空间给so。
当进程在调用so中的函数和数据时,需要知道这些函数的绝对地址.我们知道函数sum的相对地址0x568,那如何知道他的绝对地址呢。
假设so被装载的目标地址为0x10000000,那么sum的绝对地址为0x10000568.
为使so能够被多个进程link,并节省内存。so需要分离出地址无关代码。主要是希望将so指令部分不要因为装载地址的改变而改变。所以基本想法是将指令中需要被修改的部分分理出来,更数据放在一起。而将指令中不变的部分保持不变,各个进程共享。各个进程各自拥有so数据的副本。
对于add.c文件中内部的函数调用和数据访问,是地址无关的。
对于add.c中文件间函数调用和数据访问,是通过GOT来实现的。当程序要访问变量sum_count或调用print_log是,首先找到GOT,根据GOT中对应项找到地址。在so装载时,会查找sum_count和print_log的地址,并填充到GOT中,每一项对应4字节地址。具体sum_count,printf_log对应于GOT的哪一项,可以通过重定位表来查看。
GOT是放在数据段,所以它是可以在so装载时被修改的,并且每个进程都拥有副本。
查看libadd.so的section header可以看到.got的起始地址0x1fe8:
查看libadd.so的重定位表:readelf -r libadd.so. (rel.dyn 和.rel.plt分别相当于.rel.data和.rel.plt. .rel.dyn是对数据引用的修正,修正的位置位于.got。.rel.plt是对函数引用的修正,修正的位置位于.got.plt)
引用的外部变量sum_count正好在.got中。
而print_log则在.got.plt中。这是由于编译时采用了延迟绑定。
为什么要使用延迟绑定呢?在动态链接下,模块间有大量函数调用,在程序开始执行时,动态链接器会耗费大量时间去解决函数符号的查找和重定位。但是实际上程序运行时可能只有少数函数才回用到,所以会delay程序启动时间。因此ELF采用延迟绑定,只有函数第一次用到才会绑定。
采用延迟绑定后,当调用外部函数时,并不直接通过.got跳转,而是通过.got.plt跳转。每个外部函数都在.got.plt中有一项。比如print_log在.got.plt是printf_log@plt.
.got.plt的前三项是.dynamic地址,本模块的ID,_dl_runtime_resolve的地址。_dl_runtime_resolve是动态连接器函数,用来完成符号解析和重定位,将函数的真正地址放到.got表对用的项中,如print_log@GOT.
反汇编objdump -d libadd.so
在调用print_log时,跳转到print_log@plt
在.got.plt中前三项保留,jmp到表中第4项0x0c,push 0x0,0x0代表print_log在rel.plt中的下标,然后跳转到450位置。
在450位置,push .got.plt第二项模块ID,然后跳转到第三项_dl_runtime_resolve完成符号解析和重定位。
ELF--动态链接的更多相关文章
-
ELF动态链接
为什么要使用动态链接? 在现代的linux系统中,假设一个普通的程序会使用到c语言静态库至少1MB以上,那么,如果我们的机器运行100个这样的程序,就用浪费近100MB的内存:如果磁盘有2000个这样 ...
-
ELF 动态链接 - so 的 .dynamic 段
动态链接文件中最重要的段就是 .dynamic段 这个段里保存了动态链接器需要的最基本的信息 比如:1. 依赖于哪些共享对象, d_tag = DT_NEED, d_ptr 表示共享对象文件名 2 ...
-
ELF 动态链接 - so 的 重定位表
动态链接下,无论时可执行文件还是共享对象,一旦对其他共享对象有依赖,也就是所有导入的符号时,那么代码或数据中就会有对于导入符号的引用.而在编译时期这些导入符号的确切地址时未知的.只有在运行期才能确定真 ...
-
ELF 动态链接 so的动态符号表(.dynsym)
静态链接中有一个专门的段叫符号表 -- ".symtab"(Symbol Table), 里面保存了所有关于该目标文件的符号的定义和引用. 动态链接中同样有一个段叫 动态符号表 - ...
-
ELF 文件 动态链接 - 地址无关代码(GOT)
Linux 系统中,ELF动态链接文件被称为 动态共享对象(DSO,Dynamic Shared Object),简称共享对象 文件拓展名为".so" 动态链接下 一个程序可以被分 ...
-
实例分析ELF文件动态链接
参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第6章 可执行文件的装载与进程 第7章 动态链接 <Linux GOT与PLT> 开发平台 ...
-
ELF文件加载与动态链接(一)
关于ELF文件的详细介绍,推荐阅读: ELF文件格式分析 —— 滕启明.ELF文件由ELF头部.程序头部表.节区头部表以及节区4部分组成. 通过objdump工具和readelf工具,可以观察ELF文 ...
-
ELF文件加载与动态链接(二)
GOT应该保存的是puts函数的绝对虚地址,这里为什么保存的却是puts@plt的第二条指令呢? 原来“解释器”将动态库载入内存后,并没有直接将函数地址更新到GOT表中,而是在函数第一次被调用时,才会 ...
-
(九)ELF和动态链接
前言: 我们都知道我们所写的程序是被编译为一条条的CPU指令去执行的,但是在linux系统下能够运行的程序在windows环境下却运行不起来,但是我们使用的CPU明明是一样的,这又是为什么呢? 一.程 ...
-
[CSAPP-II] 链接[符号解析和重定位] 静态链接 动态链接 动态链接接口
1 平台 转http://blog.csdn.net/misskissc/article/details/43063419 1.1 硬件 Table 1. 硬件(lscpu) Architecture ...
随机推荐
-
UIDynamicAnimator UIKit动力学
也许是工作上并没有这方面的需要,对UIDynamicAnimator的了解不多.这里做简单的介绍: UIKit动力学是模拟真实世界的一些特性,主要就是UIDynamicAnimator类,通过类中的不 ...
-
String-原型属性
<script> /*将trim方法定义到字符串对象中 *使用字符串的原型属性来完成 *原型prototype:就是该对象的一个描述,该描述中如果添加新功能,那么该对象就具备这些新功能. ...
-
访问图片可以使用闭包map
1 imageView.animationImages = [ UIImage(named:"panda1"), UIImage(named:"panda2") ...
-
Android Fragment详解(五):Fragment与Activity通讯
与activity通讯 尽管fragment的实现是独立于activity的,可以被用于多个activity,但是每个activity所包含的是同一个fragment的不同的实例. Fragment可 ...
-
怎样通过js 取消input域的hidden属性使其变的可见
document.getElementById(ID).setAttribute("hidden",false);厉害了 我的哥!
-
.Net环境下调用ProtoBuf
一.什么是ProtoBuf protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台.它是一种类似于xml.json等类似作用的交互格式.由于它是一种 ...
-
Vue(二) 计算属性
模板内的表达式常用于简单的运算,当过长或逻辑复杂时,难以维护,计算属性就是解决该问题的 什么是计算属性 表达式如果过长,或逻辑更为复杂,就会变得臃肿甚至难以维护,比如: <div> {{ ...
-
Day 9 函数的初识1
def my_len(): l1 = [1,2,3,5,6] print(111) print(222) return print(333)print(my_len()) 一.函数的定义1.遇到ret ...
-
Java 继承学习
Java 继承 继承实现: 在Java中,如果实现继承的,需要使用Java关键字——extends : 被继承的类叫做超类,继承超类的类叫子类.(一个子类亦可以是另一个类的超类) class 子类 e ...
-
第二百二十八节,jQuery EasyUI,TreeGrid(树形表格)组件
jQuery EasyUI,TreeGrid(树形表格)组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解 EasyUI 中 TreeGrid(树形表格)组件的使 ...