从二进制代码来看静态链接本质

静态链接大家并不陌生,本文将从二进制代码来分析静态链接的本质。

首先列出将要静态的链接的两个源文件,它们分别是a.c和b.c,最后链接成功的文件为ab。

a.c代码如下:

extern int shared;
extern void swap(int * ,int *);

int main(){
	int a = 100;
	swap( &a, &shared );
}

b.c代码如下:

int shared = 1;

void swap( int* a, int* b )
{
	*a ^= *b ^= *a ^= *b;
}

首先使用命令gcc -c a.c 和gcc -c b.c 生成可重定位文件a.o和b.o。

接着使用命令objdump -h a.o和objdump -h b.o来查看可重定位文件的各个基本段。

分别显示如下:

图 1

图 2

这里主要注意File off和Size。a.o代码段长度为0x22;b.o代码段为0x4a,数据段为0x4。

然后使用命令ld a.o b.o -e main -o ab来链接两个可重定位文件。

使用命令objdump -h ab,得到下图:

图 3

此时我们不要关心File off,我们只关心VMA和Size。VMA是进程的虚拟空间,代码段从0x4000e8持续到0x400156,Size为0x6e。数据段从0x6001b0持续到0x6001b4,Size为0x4。

注意观察图1和图2,a.o的代码段长度为0x22,b.o的代码段长度为0x4a,两个加起来为0x6c,再加上位对齐2位,最后长度为0x6e。数据段也类似。

我们指导在a.c中shared变量和swap方法没有在该文件中定义,那么二进制代码会怎么处理呢?

使用objdump -d a.o,来查看:

图 4

13偏移处,be后面00000000表示的是shared变量的地址,由于此时还没有链接,所以使用默认值00000000,很显然是一个假值。

18偏移处,e8后面00000000表示的是swap函数的地址,由于此时还没有链接,所以使用默认值00000000,很显然是一个假值。

同样使用命令objdump -d b.o,来查看:

图 5

这个函数没有需要重定位的函数和变量。

我们使用objdump -d ab,来查看链接后的文件ab:

图 6

为了代码对齐,在main函数后面多了两条空指令,所以ab的长度是a.o和b.o长度之和再加上2。

我们观察到0x4000fb处be后面的数据已经不是前面的0x00000000,而是0x6001b0,我们知道这个数据表示shared变量的地址,为什么是这个值呢?请看图3,数据段的起始位置是0x6001b0,shared是唯一个变量,所以0x6001b0就是shared变量的地址。

在0x400103处e8后面的数据也已经不是前面的0x00000000,而是0x00000004。请看图3或者图6,代码段起始位置为0x4000e8,swap函数的地址为0x40010c。那么e8后面为什么是0x00000004呢?因为call转到的真正地址是call下一条指令地址0x400108+0x4,最后的真正的地址为0x40010c,正好是swap的地址。

至此,本文分析完毕,参考程序员的自我修养

时间: 2024-10-09 02:40:30

从二进制代码来看静态链接本质的相关文章

静态链接

静态链接 1 编译和链接 1.1 被隐藏了的过程 例如: #include<stdio.h> int main() { printf("Hello World\n"); return 0; } 在Linux下,使用GCC编译: gcc hello.c ./a.out Hello World 事实上,上述过程由4个步骤,分别是预处理.编译.汇编和链接,如图所示: 1.1.1 预编译 首先是源代码hello.c和相关的头文件,如stdio.h等被预编译器cpp预编译成一个.i文

计算机科学基础知识(三)静态库和静态链接

三.将relocatable object file静态链接成可执行文件 将relocatable object file链接成可执行文件分成两步,第一步是符号分析(symbol resolution),第二步是符号重新定位(Relocation).本章主要描述这两个过程,为了完整性,静态库的概念也会在本章提及. 1.为什么会提出静态库的概念? 程序逻辑有共同的需求,例如数学库.字符串库等,如果每个程序员在撰写这些代码逻辑的时候都需要自己重新写那么该是多么麻烦的事情,而且容易出错,如果有现成的,

程序的静态链接

程序的静态链接 程序的产生 程序是由程序员编写,经过编译链接过程,最终能够在计算机中运行的东西.本质上来说编译链接过程其实就是将由人能看懂的代码段翻译成机器能看懂的代码段,然后指导机器的运行,比起程序在机器中被运行,博主更喜欢程序指导机器运行这样的说法. 编译链接事实上分为4个过程:预编译.编译.汇编.链接,在这里我们笼统地将其分为两个过程:编译和链接,编译包含预编译.编译.汇编. 编译是将程序员写的代码翻译成机器码,即机器能够解析的二进制码,生成二进制目标文件,既然编译过程已经生成了机器能够解

[C] linux静态链接库与动态链接库详解

http://blog.chinaunix.net/u2/76292/showart.php?id=1274181 一顺便说说了哦  通常情况下,对函数库的链接是放在编译时期(compile time)完成的.所有相关的对象文件(object file)与牵涉到的函数库(library)被链接合成一个可执行文件(executable file).程序在运行时,与函数库再无瓜葛,因为所有需要的函数已拷贝到自己门下.所以这些函数库被成为静态库(static libaray),通常文件名为"libxx

Linux下 静态链接库 和 动态链接库

先来说说C/C++编译过程 编译: 检查语句符号定义,将C/C++代码翻译生成中间语言. 链接: 将中间代码整合,生成可执行的二进制代码. 简单的说,库文件都是一种特殊的中间语言文件,静态库还是一种特殊格式的归档文件(打包的文件). 使用静态库: 1. 先编写库函数 1 #ifndef _PRINT_TEST_H_ 2 3 #define _PRINT_TEST_H_ 4 #ifdef __cplusplus 5 extern "C" 6 { 7 #endif 8 9 extern i

实例分析ELF文件静态链接

1.ELF文件格式概貌 readelf -h 查看elf文件头部信息可以看到Type值有三种:REL,EXEC,DYN. REL文件是只被编译没有被链接过的文件,其格式属于左边一种,elf header+section1,2,3...+section header table,每个section对应一个section header table entry,section header table为各个section提供索引.没有被链接过的文件没有program header,不能被加载到内存中运

C++中的动态链接库和静态链接库

转自 作者:吴秦出处:http://www.cnblogs.com/skynet/ 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书<程序员的自我修养--链接.装载与库>. 什么是库 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式

C语言之静态链接库和动态链接库

1:静态链接库 比较早出现的是静态链接库.静态库其实就是商业公司将自己的函数库源代码经过只编译不连接形成.o的目标文件,然后用ar工具将.o文件归档成.a的归档文件(.a的归档文件又叫静态链接库文件).商业公司通过发布.a库文件和.h头文件来提供静态库给客户使用:客户拿到.a和.h文件后,通过.h头文件得知库中的库函数的原型,然后在自己的.c文件中直接调用这些库文件,在连接的时候链接器会去.a文件中拿出被调用的那个函数的编译后的.o二进制代码段链接进去形成最终的可执行程序. 2:动态链接库 动态

boost静态链接的问题 -lgcc_s

在使用gcc/g++ 编译程序时我们希望指向一些库是使用静态的链接方式. 另外的一些是动态的方式. 我以boost 为例. 如果我们要使用静态库则是这样的: # g++ main.cpp -lpthread /usr/lib64/libboost_thread.a /usr/lib64/libboost_system.a 静态库直接写路径. 动态前面加-l  这样也可以实现. 但有没有更好的办法呢. 有. 先参考个帖子:http://stackoverflow.com/questions/369