静态连接浅析
2015-11-29 21:14
211 查看
我们知道一个程序在执行时需要经过编译,链接,运行。这里来分析一下关于静态链接的内容。
我们先来看一下一个程序的执行过程:
在Linux下gcc 会直接链接成可执行文件a.out,而gcc -c 就是编译各个源文件成目标文件,即.o结尾的文件。
如下图,我们有这样三个简单的函数:
main()函数
add函数
sub函数
在Linux下,我们构造了这样三个函数,命名分别为main.c, add.c, sub.c 。其中在main.c中调用了三个函数,分别为add,sub,printf。
首先我们用命令:
gcc -c main.c
来编译main.c这个源程序,得到main.o,同时用命令:
objdump -d main.o
来查看main.c编译好的程序的汇编代码:
由上图,观察可以看出在main函数中有三个call 命令,这也就是那三个函数的调用指令。和我们之前所预想的一样,分别为
18:e8 fc ff ff ff call 19 <main+0x19>
30:e8 fc ff ff ff call 31 <main+0x31>
50:e8 fc ff ff ff call 51 <main+0x51>
再仔细观察会发现这三个call指令的指令码是一样的,均是 e8 fc ff ff ff 即-4。这样就是在下一条指令地址减去4,又回到了这里。其实这里的-4起到的就是一个标记作用。这样我们可以解开这次的主题了:静态链接。
在静态连接下我们用命令编译main.c后得到如下图所指示的一个目标文件:
这就是main.o,这个目标文件从地址0x00000000开始,但遇到add函数时并不知道它在哪里,就把它做了一个标记,告诉这是一个函数调用,下面的call sub 和call printf相同。
同时编译好add.c和sub.c成目标文件。我们进行链接。链接的命令是ld。其实链接就是扫描整个main.o代码。扫描到指令码为e8 fc ff ff ff时就将所需要的文件加入进去,再main.o中调用函数就像一个个补丁一样,等待着链接器去修补,链接器做的事很脏很累。它需要一个一个的去寻找补丁,需要把每一个库加载到里边来寻找所需要的。这样等链接完成就是如下这样:
这样就完成了链接。当然系统怎么知道哪个洞对应哪个补丁呢?这就用上了符号表了。在链接时,会有一个符号表,大概就是这个样子:
即key -value的对来确定怎样对应链接。形成可执行文件a.out。来看一下a.out的汇编代码。
如上图这样,链接好的a.out中的三个call 就是内存中的实际地址。
参考文献:
《程序员的自我修养》
《UNIX高级环境编程》
我们先来看一下一个程序的执行过程:
在Linux下gcc 会直接链接成可执行文件a.out,而gcc -c 就是编译各个源文件成目标文件,即.o结尾的文件。
如下图,我们有这样三个简单的函数:
main()函数
add函数
sub函数
在Linux下,我们构造了这样三个函数,命名分别为main.c, add.c, sub.c 。其中在main.c中调用了三个函数,分别为add,sub,printf。
首先我们用命令:
gcc -c main.c
来编译main.c这个源程序,得到main.o,同时用命令:
objdump -d main.o
来查看main.c编译好的程序的汇编代码:
由上图,观察可以看出在main函数中有三个call 命令,这也就是那三个函数的调用指令。和我们之前所预想的一样,分别为
18:e8 fc ff ff ff call 19 <main+0x19>
30:e8 fc ff ff ff call 31 <main+0x31>
50:e8 fc ff ff ff call 51 <main+0x51>
再仔细观察会发现这三个call指令的指令码是一样的,均是 e8 fc ff ff ff 即-4。这样就是在下一条指令地址减去4,又回到了这里。其实这里的-4起到的就是一个标记作用。这样我们可以解开这次的主题了:静态链接。
在静态连接下我们用命令编译main.c后得到如下图所指示的一个目标文件:
这就是main.o,这个目标文件从地址0x00000000开始,但遇到add函数时并不知道它在哪里,就把它做了一个标记,告诉这是一个函数调用,下面的call sub 和call printf相同。
同时编译好add.c和sub.c成目标文件。我们进行链接。链接的命令是ld。其实链接就是扫描整个main.o代码。扫描到指令码为e8 fc ff ff ff时就将所需要的文件加入进去,再main.o中调用函数就像一个个补丁一样,等待着链接器去修补,链接器做的事很脏很累。它需要一个一个的去寻找补丁,需要把每一个库加载到里边来寻找所需要的。这样等链接完成就是如下这样:
这样就完成了链接。当然系统怎么知道哪个洞对应哪个补丁呢?这就用上了符号表了。在链接时,会有一个符号表,大概就是这个样子:
即key -value的对来确定怎样对应链接。形成可执行文件a.out。来看一下a.out的汇编代码。
如上图这样,链接好的a.out中的三个call 就是内存中的实际地址。
参考文献:
《程序员的自我修养》
《UNIX高级环境编程》
相关文章推荐
- Android开发流程(句句真言)
- 101 Symmetric Tree
- 51单片机产生PWM波
- Alamofire,SwiftJSON,MJExtension,解析Json获取对象,带参数上传图片
- iOS —— CALayer 创建图层
- js 循环
- web.xml
- 第一次来博客
- Merge Sorted Array II
- [BZOJ1030][JSOI2007]文本生成器
- Pull解析
- UI第三MVC
- 算法之4--拓扑排序
- Java自定义注解及注解的使用
- Manifest与TypeTag
- IOS工作小结
- Http长连接
- 存储过程的简化--只记录了问题--项目结束后一并优化
- MySQL递归查询树状表的子节点、父节点
- 关于lua语言及热更新的研究一