对前面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--动态链接的更多相关文章

  1. ELF动态链接

    为什么要使用动态链接? 在现代的linux系统中,假设一个普通的程序会使用到c语言静态库至少1MB以上,那么,如果我们的机器运行100个这样的程序,就用浪费近100MB的内存:如果磁盘有2000个这样 ...

  2. ELF 动态链接 - so 的 .dynamic 段

    动态链接文件中最重要的段就是 .dynamic段 这个段里保存了动态链接器需要的最基本的信息 比如:1.  依赖于哪些共享对象, d_tag = DT_NEED,  d_ptr 表示共享对象文件名 2 ...

  3. ELF 动态链接 - so 的 重定位表

    动态链接下,无论时可执行文件还是共享对象,一旦对其他共享对象有依赖,也就是所有导入的符号时,那么代码或数据中就会有对于导入符号的引用.而在编译时期这些导入符号的确切地址时未知的.只有在运行期才能确定真 ...

  4. ELF 动态链接 so的动态符号表(.dynsym)

    静态链接中有一个专门的段叫符号表 -- ".symtab"(Symbol Table), 里面保存了所有关于该目标文件的符号的定义和引用. 动态链接中同样有一个段叫 动态符号表 - ...

  5. ELF 文件 动态链接 - 地址无关代码(GOT)

    Linux 系统中,ELF动态链接文件被称为 动态共享对象(DSO,Dynamic Shared Object),简称共享对象 文件拓展名为".so" 动态链接下 一个程序可以被分 ...

  6. 实例分析ELF文件动态链接

    参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第6章 可执行文件的装载与进程 第7章 动态链接 <Linux GOT与PLT> 开发平台 ...

  7. ELF文件加载与动态链接(一)

    关于ELF文件的详细介绍,推荐阅读: ELF文件格式分析 —— 滕启明.ELF文件由ELF头部.程序头部表.节区头部表以及节区4部分组成. 通过objdump工具和readelf工具,可以观察ELF文 ...

  8. ELF文件加载与动态链接(二)

    GOT应该保存的是puts函数的绝对虚地址,这里为什么保存的却是puts@plt的第二条指令呢? 原来“解释器”将动态库载入内存后,并没有直接将函数地址更新到GOT表中,而是在函数第一次被调用时,才会 ...

  9. (九)ELF和动态链接

    前言: 我们都知道我们所写的程序是被编译为一条条的CPU指令去执行的,但是在linux系统下能够运行的程序在windows环境下却运行不起来,但是我们使用的CPU明明是一样的,这又是为什么呢? 一.程 ...

  10. [CSAPP-II] 链接[符号解析和重定位] 静态链接 动态链接 动态链接接口

    1 平台 转http://blog.csdn.net/misskissc/article/details/43063419 1.1 硬件 Table 1. 硬件(lscpu) Architecture ...

随机推荐

  1. Python网页解析

    续上篇文章,网页抓取到手之后就是解析网页了. 在Python中解析网页的库不少,我最开始使用的是BeautifulSoup,貌似这个也是Python中最知名的HTML解析库.它主要的特点就是容错性很好 ...

  2. ORACLE学习笔记 索引和约束

    /*** 约束 ***/ * 如果某个约束只作用于单独的字段,即可以在字段级定义约束,也可以在表级定义约 束,但如果某个约束作用于多个字段,  必须在表级定义约束* 在定义约束时可以通过CONSTRA ...

  3. AJAX overrideMimeType作用

    我们经常在AJAX代码中发现如下代码:   if (http_request.overrideMimeType) {    http_request.overrideMimeType("te ...

  4. swfupload 参数说明

    一.配置参数对象中的常用属性及说明 属性 类型 默认值 描述 upload_url String   处理上传文件的服务器端页面的url地址,可以是绝对地址,也可以是相对地址,当为相对地址时相对的是当 ...

  5. C#实现不安装Oracle客户端访问远程服务器数据!!

    概述: C#通过使用ADO的方式在未安装Oracle数据库的前提下,客户端程序远程访问服务器,会出现:“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或 ...

  6. [再寄小读者之数学篇](2014-11-19 $\tan x/x$ 在 $(0,\pi/2)$ 上递增)

    $$\bex \frac{\tan x}{x}\nearrow. \eex$$ Ref. [Proof Without Words: Monotonicity of $\tan x/x$ on $(0 ...

  7. php 采集程序 宋正河

    本程序主要是通过php采集网页信息,程序自动存储采集进度,采用phpquery简化元素节点匹配,采集到的内容直接入库 你要做的只是写好采集语法,本程序适合有一定php基础的人使用!当然你也可以进行修改 ...

  8. C# 使用Linq递归查询数据库遇到的问题及解决方法

    User表通常是我们在写"XX管理系统"项目时必须要用到的,有的情况下人员的分类属于树形结构,就是除了最高层和最低层,中间层都有相对的父和子,设计数据库的时候,我们通常会加一个pa ...

  9. Python的descriptor (2)

    前面说了descriptor,这个东西其实和Java的setter,getter有点像.但这个descriptor和上文中我们开始提到的函数方法这些东西有什么关系呢? 所有的函数都可以是descrip ...

  10. 爬虫技术之——bloom filter(含java代码)

    在爬虫系统中,在内存中维护着两个关于URL的队列,ToDo队列和Visited队列,ToDo队列存放的是爬虫从已经爬取的网页中解析出来的即将爬取的URL,但是网页是互联的,很可能解析出来的URL是已经 ...