关于C语言静态链接的个人理解,欢迎指正
摘要:本篇主要介绍在静态链接中多个文件合并、地址确定、符号解析和重定位相关问题,以GCC编译器为例。
- 空间与地址分配:(1)扫描各个输入文件获得各个段的长度、属性、位置等信息;(2)收集各个文件的符号表并建立统一的全局符号表。这一步将根据各个段的信息计算出合并后各个段的长度和位置,并建立映射关系(我的理解是更新段表的信息,段表描述ELF文件包含的所有段的信息,比如段名、长度、偏移量等)。
- 符号解析与重定位:这一步至关重要,由于上一步对相似段进行合并之后,原先的符号表信息已经过时,并且原先文件中代码的地址并没有映射到虚拟地址空间中,所以这一步要完成符号解析与重定位、调整代码中的地址等。
int shared = ;
void swap(int* a, int* b)
{
*a ^= *b ^= *a ^= *b;
}
extern int shared;
int main(int argc, char** argv)
{
int a = ;
swap(&a, &shared);
return ;
}
~





可以看到a.o和b.o他们的起始地址都为0,而可执行文件ab的起始地址则是从0x0804000起(.text段之前还有文件头)。
重点和难点在于符号解析和重定位,即要更新文件合并后的总全局符号表,在构建全局符号表时即完成符号解析,重定位需要在符号解析后完成。在目标文件的结构中有一种section叫重定位表,各个段中如果有需要重定位的符号那么就会有相应的重定位表,如.text的重定位表是.rel.text。由于在a.c代码中用到了shared和swap这两个符号都属于b.c文件中的定义,链接时需要重定位,所以a.o中的的text段就会有相应的重定位表。同样用objdump可以查看目标文件的重定位表内容:
我们可以看到有两行是关于需要重定位符号shared和swap的描述,其中OFFSET表示它们在a.o文件中的偏移值,TYPE表示重定位时对指令的修正方式,下面是书中对其解释的相应的图表:

上面提到的r_offset和r_info是重定位表的结构中的变量,摘取书中的解释:

所以我的理解是A就是还未重定位时,符号shared和swap的地址,P即是在可执行文件ab中需要修改处的偏移值,而S是b.o和a.o合并后符号shared和swap的实际地址。具体该怎么算继续往下面看。
我们将a.o进行反汇编得到:

那么怎样找到代码a.c中对shared和swap的使用是上图哪两条指令呢?因为shared和swap是在b.c中定义的,在链接时a.o中对符号shared和swap必然要重定位,因此a.o中需要重定位的位置即是使用这两个符号的位置,所以由上面text段的重定位表中信息可知在偏移量为11和20处的两条指令即是使用它们地方(因为需要重定位的偏移量刚好在这两条指令中间)。
我们来分析这两条指令,寄存器esp是专门用来作为栈顶指针使用的,所以mov $0x0, 0x4(%esp)是把0储存在偏移栈顶4个字节的位置,那这个0值又是什么呢?当然不是shared值啦,shared值是已知的只是不知道它存储在什么地方,为什么不知道它在什么地方呢?是因为在链接之前还没有对它重定位,所以这个0值应该是未重定位之前shared地址的默认值(这里这样解释只是我自认为的一种通俗表达,我的另一种理解是在编译层面和语言层面shared是不同的东西,在语言层面shared是一个变量,对它的操作是直接对它所在内存区域的操作,而在编译层面shared是内存中某块地址空间的引用,所以在符号表中shared的值是那块内存的地址,因此0值就是shared的值)。这个0又是怎么来的,注意上图是反汇编,所以汇编代码是由机器指令反编译得到的,偏移量11处的机器指令是0xc7 44 24 04 00 00 00 00,前4个字节是指令码,后四个字节是符号shared对应的值,即0。另外一条指令call 21 <...>,在汇编(其他语言也是)中函数名就是函数在内存中的起始地址,所以假设21就是swap的起始地址,那么call 21和call swap是等价的,现在是swap不在代码a.c中定义,这样的话就不能使用call swap了,而是给swap起始地址一个默认值(21),使用call 21。这个21又是怎样得到的呢?看与这条指令对应的机器指令,e8 fc ff ff ff(5个字节长度),书中对这条机器指令的解释是:0xe8是操作码,在Intel的IA-32体系中表示这是一条近址相对位移调用指令,操作码后面的四个字节就是被调用函数的相对于调用指令的下一条指令的偏移量,在没有重定位前默认为0xfc ff ff ff(小端字节表示法,代表-4的补码),所以21是(25-4)得来的,这是个假地址。
现在再对链接a.o b.o 输出的可执行文件ab进行反汇编。

由重定位表的偏移量计算可知,现在关于符号shared和swap的使用指令对应上图的偏移量为80480a5和80480b4两条指令。上图我们看到的结果是重定位后的,重定位时我们要修改的值是偏移量80480a5处的后四个字节和偏移量804800b4处后四个字节。根据上文提到的公式S+A和S+A-P,就可以算出重定位后的值。
首先看符号shared重定位后的值怎么算。先查看可执行文件ab得到合并后变量shared的地址,如图

上图数据段在虚拟地址空间中起始地址是0x08049158,因为这个可执行文件中data段中就只有一个数据变量所以这个地址也是shared的地址,即S=0x08049158,重定位前地址是0x0,即A=0x0,所以S+A=0x08049158,在内存中以小端表示法存储时即为58 91 04 08。你可能会问如果不只一个全局变量时,我又怎么知道他们合并后的地址,注意合并后的地址都在符号解析后的全局符号表中,链接器是知道的。
再看swap重定位后的值怎么算,在上面对可执行文件ab反汇编时我们看到函数swap的入口地址为0x080480c0,即S=0x080480c0,重定位前call汇编代码对应的机器指令后四个字节的值是ff ff ff fc(-4),即A=-4,P是被修正的位置,为0x080480b5,由公式S+A-P(c0-4-b5=7)得重定位后修改为07 00 00 00(小端表示法)。
以上是我在看《程序员的自我修养--链接、装载与库》一书中第四章前2节的个人理解,限于个人水平问题,有些地方的理解可能有偏差,欢迎指正。
关于C语言静态链接的个人理解,欢迎指正的更多相关文章
- Linux环境下c语言静态链接库和动态链接库创建和使用
库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀. 面对比一下两者: 静态链接库:当要使用时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功, ...
- c语言静态链接库
1 获得lib文件 vc++ 6.0中 新建 Win32 Static Library项目,命名为libTest 新建lib.h文件,代码如下 #ifndef LIB_H #define LIB_H ...
- C语言编写静态链接库及其使用
本篇讲述使用C语言编写静态链接库,而且使用C和C++的方式来调用等. 一.静态库程序:执行时不独立存在,链接到可执行文件或者动态库中,目标程序的归档. 1.用C编写静态库步骤 a.建立项目(Win32 ...
- C语言动静态链接库使用(笔记)
看了视频一直没空写........... C静态链接库不用说了跟你写在cpp文件里的函数一样不会有单独的模块 不再赘述生活中用的比较少 例子 .h文件 int Plus(int x, int y); ...
- vc下的静态链接库与动态链接库(一)
一.静态库与动态库的区别 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Lib ...
- C语言编译链接
转载请标明: 编译链接是使用高级语言编程所必须的操作,一个源程序只有经过编译.链接操作以后才可以变成计算机可以理解并执行的二进制可执行文件. 编译是指根据用户写的源程序代码,经过词法和语法分析,将高级 ...
- COM编程之五 动静态链接
[1]静态链接 静态链接是指由链接器在链接时将库的内容加入到可执行程序中的做法. 链接器是一个独立程序,将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一块生成可执行程序. 函数和数据被编译 ...
- dll和lib(包括静态链接库和与dll同时生成的lib)
转:http://blog.csdn.net/galaxy_li/article/details/7411956 1:神马是Dll和Lib,神马是静态链接和动态链接 大家都懂的,DLL就是动态链接库, ...
- GCC编译过程与动态链接库和静态链接库
1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可 ...
随机推荐
- 帮小黎解决问题C++巩固获得数字每个位置上的数
现在有一个数字 a= 12345; 想要取得这个数字上的没一个数字 使用 除法 +模除 的方法可以获得 原理:除(/)得到的是商 模除(%)的到的是余数 采用这种方式,先将要求的数的某一位 ...
- Word2Vec之Deep Learning in NLP (一)词向量和语言模型
转自licstar,真心觉得不错,可惜自己有些东西没有看懂 这篇博客是我看了半年的论文后,自己对 Deep Learning 在 NLP 领域中应用的理解和总结,在此分享.其中必然有局限性,欢迎各种交 ...
- 感知机(perceptron)
二类分类的线性分类模型,属于判别模型,利用梯度下降法对损失函数进行极小化求得感知机模型分为原始形式和对偶形式,是神经网络和支持向量机的基础 由输入控件到输出控件的如下函数: f(x)=sign(W.X ...
- VS2010中使用QtOpenGL出现 unresolved external symbol __imp__glClear@4 referenced in function之类的错误
描述: 链接了QtOpenGL4.lib QtOpend4.lib的库啊,居然还是发生此错误. 原因是没有链接OpenGL32.lib这个库.所以,要添加这个lib 重新rebuild的一下,此类的错 ...
- android官方侧滑菜单DrawerLayout详解
drawerLayout是Support Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产物.d ...
- HDu 5433 Xiao Ming climbing (BFS)
题意:小明因为受到大魔王的诅咒,被困到了一座荒无人烟的山上并无法脱离.这座山很奇怪: 这座山的底面是矩形的,而且矩形的每一小块都有一个特定的坐标(x,y)和一个高度H. 为了逃离这座山,小明必须找到大 ...
- 第06讲- DDMS中logcat的使用
1.DDMS使用 )Device选项卡 Device中罗列了Emulator中所有的进程,选项卡右上角那一排按钮分别为:调试进程.更新进程.更新进程堆栈信息.停止某个进程. )Threads选项卡 ...
- 《Java程序员面试笔试宝典》之 什么是AOP
AOP(Aspect-Oriented Programming,面向切面编程)是对面向对象开发的一种补充,它允许开发人员在不改变原来模型的基础上动态地修改模型从而满足新的需求.例如,在不改变原来业务逻 ...
- 使用Open Flash Chart(OFC)制作图表(Struts2处理)
Java开源项目中制作图表比较出色的就是JFreeChart了,相信大家都听说过,它不仅可以做出非常漂亮的柱状图,饼状图,折线图基本图形之外,还能制作甘特图,仪表盘等图表.在Web应用中可以为项目增色 ...
- HDU 4362 Dragon Ball 线段树
#include <cstdio> #include <cstring> #include <cmath> #include <queue> #incl ...