首页 > 代码库 > 从二进制代码来看静态链接本质

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

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


    首先列出将要静态的链接的两个源文件,它们分别是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.cgcc -c b.c 生成可重定位文件a.o和b.o。

    接着使用命令objdump -h a.oobjdump -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的地址。


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

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