基本修养实战篇(四) 链接的符号解析与重定位

这次我们来看一个新的例子

a.c的内容如下:

extern int shared;

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

b.c的内容如下:

int first_var = 2;
int shared = 1; void swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp; }

gcc -c a.c -o a.o

gcc -c b.c -o b.o

回顾之前的内容,我们先看一下a.o中的符号表:

readelf -a a.o

基本修养实战篇(四) 链接的符号解析与重定位

 

 Bind类型为LOCAL的,我们都不需要看了,重点看3个GLOBAL的,其中main的Ndx不是UND

而是1,是啥意思来着。是指这个symbol所在段在段表中的索引为1,也就是text段

基本修养实战篇(四) 链接的符号解析与重定位

 

 看到swap和shared是NOTYPE,也即未知类型的全局符号。

然后main的size是56指的是函数指令所占的字节数,Value表示该符号相对于代码段的起始位置的偏移量

readelf -a b.o

基本修养实战篇(四) 链接的符号解析与重定位

 

 b.o中的shared 符号,在data段中偏移4个字节的位置,swap的size是76

我们是用链接器ld把a.o和b.o链接起来:

ld a.o b.o  -e main -o ab:

-e: 表示将main做为程序入口,默认的入口是_start

链接完成后输出ab, 我们执行了一下ab, 发现产生了段错误,crash了。

但是我用gcc a.c b.c -o ab1, 却能够正常运行,比较两个输出文件的size, ld链接的结果只有1224字节,但是gcc的输出结果却有8220字节,让人不禁再次陷入了深思。为什么会crash, 以及链接后的结果为什么相差这么大,我们继续往下分析。

 

ulimit -c unlimited命令,开启core dump功能,并且不限制生成core dump文件的大小

 

我们这个调试的问题,后面再详细说,这里继续分析一下链接的相关问题

基本修养实战篇(四) 链接的符号解析与重定位

 

 首先看一下38 + 4C = 0x84, 不错,text段的确被合并了。

然后看一下ab中的text的VMA和LMA, 现在有了具体的值,变成了0x00010094,

VMA表示这个段在内存中的运行地址,LMA表示这个text段的加载地址。

也就是说这段代码映像会被先加载到LMA, 然后运行前还要拷贝到VMA处(如果VMA不等于LMA的话)

【上述结论结合bootloader的相关知识点学习】

那么还有一个问题,为啥text段会被分配到0x00010094,data分配到0x00020118呢?

这涉及到操作系统的进程虚拟地址空间分配的规则。

就是在ld脚本中啦,ld脚本就规定程序和数据在bin文件里面是什么存储的,以及运行时在rom和ram中是怎么存储的文件。

这里就引入了链接脚本的概念,同时也是bootloader中会涉及到的概念,也就是说就是链接脚本中指定的这个地址。

 

ldd --help 中有一个-T 选项,就是读取链接脚本的,也验证了我们的说法

 

链接器在扫描和分配空间完成之后,会根据链接脚本中指定的位置,把对应的text和data段放上去。同时因为各个符号在段内的相对位置是固定的,这样只要给每个符号加上一个 相对text段首大偏移 + 自己所在段的偏移就可以得到每一个符号的绝对地址

 

比如,swap的地址就是 main + 0x38, 也就是 10094h + 38h  = 100cc h


 

在完成空间和地址的分配步骤以后,链接器就进入了符号解析与重定位的步骤。

我们先使用objdump -d a.o 看一下在编译期间,编译器对外部符号的处理

基本修养实战篇(四) 链接的符号解析与重定位

 

 首先可以看到,main的起始地址是0, 只有等到空间分配完成后,各个函数才会确定自己在虚拟地址空间中的位置

 

然后是bl跳转语句。关于bl指令,我目前没有仔细研究。我们重点对比,ab中的这个地方,就知道,对应的地址被修改了。

为了辅助链接器完成这个功能,在elf中还有一个叫重定位表的东西,我们在前面提起过.rel.text 和 .rel.data的段就是重定位表,也叫

重定位段。

 

objdump -r a.o

基本修养实战篇(四) 链接的符号解析与重定位

 

 这个命令可以查看a.o中所有需要重定位的地方。

首先指明下面的待重定位符号在text段中,偏移为0x20的地方需要对一个叫swap的符号进行重定位,偏移为0x34的地方需要对shared这个符号

重定位。 与反汇编的代码段对照一下就可以看的更加清晰

基本修养实战篇(四) 链接的符号解析与重定位

 

上一篇:小白自制Linux开发板 五. Debian文件系统制作,以及WIFI配置、交换分区配置


下一篇:C# type对象