侵权投诉
订阅
纠错
加入自媒体

【图片+代码】:GCC 链接过程中的【重定位】过程分析

2022-03-07 11:39
道哥分享
关注


符号表信息

指令:readelf -s main.o

重点看一下黄色矩形中的3个符号。

main符号:

1. Size=50: 长度是 30 个字节,也就对应着代码段的长度 0x32 ;

2. Type=FUNC:说明这是一个函数;

3. Bind=GLOBAL:说明这个符号是全局可见的,也就是在其他文件中可以调用;

4. Ndx=1:说明这个符号是属于第 1 个 段中,就是代码段(.text);

下面两个符号SubData和SubFunc,他们的Ndx都是UND,表示这2个符号被main.o使用,但是定义在其他文件中。

我们知道,当链接成可执行文件时,所有的符号都必须有确定的地址(虚拟地址),所以链接器就需要在链接的过程中找到这2个符号在可执行文件中的地址,然后把这两个地址填写到main的代码段中。

可以先来看一下main.o的反汇编代码:

指令:objdump -d main.o

黄色矩形框中是把数值0存储到eax寄存器中,然后把eax 压到栈中,然后红色矩形框调用了一个函数。

从示例代码(.c文件)中可知:main函数在调用sub.c中的SubFunc函数时,传入了变量SubData。

黄色部分的00 00 00 00就应该是符号SubData的地址,只不过此时main.o还不知道这个符号的将会被链接器安排在什么地址,所以只能空着(以4个字节的00来占位)。

红色部分的调用(call)地址为什么是fc ff ff ff?

按照小端格式计算一下:0xfffffffc,十进制的值就是-4,为什么设置成-4呢?

对于x86平台的ELF格式来说,对地址进行修正的方式有2种:绝对寻址和相对寻址。

绝对寻址

对于SubData符号就是绝对寻址,在链接成可执行文件时,这个地址在代码段中偏移0x12个字节(黄色矩形框指令码偏移0x11个字节,跨过一个字节的指令码a1就是0x12个字节),这个地方4个字节的当前值是 00 00 00 00。

链接器在修正的时候(就是链接成可执行文件的时候),会把这4个字节修改为SubData变量在可执行文件中的实际地址(虚拟地址)。

相对寻址

红色矩形框中的函数调用(SubFunc符号),就是相对寻址,就是说:当CPU执行到这条指令的时候,把PC寄存中的值加上这个偏移地址,就是被调用对象的实际地址。

链接器在重定位的时候,目的就是计算出相对地址,然后替换掉fc ff ff ff这四个字节。

PC寄存器中的值是确定的,当call这条指令被CPU取到之后,PC寄存器被自动增加,指向下一条指令的开始地址(偏移0x1f地址处)。

实际地址 = PC值 + xxxx_xxxx,所以得到:xxxx_xxxx = 实际地址 - PC值。

而PC值与 xxxx_xxxx 所在的地址之间是有关系的:PC值 + (-4)就得到 xxxx_xxxx 所在的地址,因此在main.o中预先在这个地址处填 fc ff ff ff(-4)。

问题来了,链接器怎么知道main.o中代码段的这两个地方,需要进行地址修正?

这就是下面介绍的重定位表的作用了!

重定位表信息

指令:objdump -r main.o

重定位表就表示: 该目标文件中,有哪些符号需要在链接的时候进行地址重定位。

从图中黄色矩形框可以看出:main.o中代码段(.text)的 SubData和SubFunc这 2 个符号都需要链接器对它进行重定位。

TYPE列:R_386_32表示绝对寻址, R_386_PC32 表示相对寻址; OFFSET列表示需要重定位的符号在main.o文件代码段中的偏移位置。

刚才已经看了main.o的反汇编代码,可以看到偏移0x12 和 0x1b的地方,就是需要进行地址重定位的两个符号。

可执行程序 main

有了 2 个目标文件:sub.o和main.o,就可以链接得到可执行程序了:

$ ld -m elf_i386 main.o sub.o -e main -o main

段信息

使用readelf工具来看一下main可执行文件中的段信息(指令:readelf -S main):

1. 红色矩形框是代码段(.text),链接器把它放在虚拟地址 0x0804_8094;

2. 黄色矩形框是数据段(.data),链接器把它放在虚拟地址 0x0804_9138;

从段信息中可以看到main文件中代码段和数据段的布局如下:

可执行程序main是由main.o和sub.o这两个目标文件组成的,所以main中的代码段是由main.o中的代码段和sub.o中的代码段组合得到的;对于数据段,由于 main.o中数据段的长度为0,所以main中的数据段就是sub.o中的数据段(长度为4),如下图所示:

<上一页  1  2  3  下一页>  
声明: 本文由入驻维科号的作者撰写,观点仅代表作者本人,不代表OFweek立场。如有侵权或其他问题,请联系举报。

发表评论

0条评论,0人参与

请输入评论内容...

请输入评论/评论长度6~500个字

您提交的评论过于频繁,请输入验证码继续

暂无评论

暂无评论

    电子工程 猎头职位 更多
    扫码关注公众号
    OFweek电子工程网
    获取更多精彩内容
    文章纠错
    x
    *文字标题:
    *纠错内容:
    联系邮箱:
    *验 证 码:

    粤公网安备 44030502002758号