读书-程序员的自我修养-链接、封装与库(8: 第四章:静态链接(1)空间与地址分配:静态链接,地址空间,链接步骤

1. 什么是静态链接?

1.1 定义

当有两个目标文件时,如何将它们链接起来形成一个可执行文件?
这就是静态链接。

1.2 代码例子说明

a.c

extern int iShared;
int main()
{
	int a=1;
	swap(&a, &iShared);
}

b.c

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

root@ubuntu-admin-a1:/home/4Chapter# gcc -c a.c b.c
root@ubuntu-admin-a1:/home/4Chapter# ls
a.c a.o b.c b.o
root@ubuntu-admin-a1:/home/4Chapter# gcc -o ab a.o b.o
root@ubuntu-admin-a1:/home/4Chapter# ls
ab a.c a.o b.c b.o
root@ubuntu-admin-a1:/home/4Chapter#

2. 空间与地址分配

例子 a.c,b.c 的目标文件就是 a.o b.o,输出可执行文件就是ab。
那么对于多个输入目标文件,链接器是如何将它们的各个段合并到输出文件的?

2.1 按序叠加

一个简单的方案就是将输入的目标文件按照次序叠加起来。

缺点:
输出文件有很多零散的段。
每个段都需要对齐,浪费空间。
在这里插入图片描述

2.2 相似段合并

就是将输入文件的 .text合并到输出文件的.text段……等等
其中,.bss段在目标文件和可执行文件中并不占用文件的空间,
但是在装载时占用地址空间。
在这里插入图片描述

2.3 地址和空间什么意思?

其中,链接器为目标文件分配地址和空间,这里的地址和空间什么意思?

  1. 第一个是在输出的可执行文件中的空间;
  2. 第二个是在装载后的虚拟地址中的虚拟地址空间

解释:
.text段和.data段来说,他们在文件和虚拟地址中都要分配空间。
.bss段只需要虚拟地址空间。

事实上,我们这里谈到的空间分配只关注于虚拟地址空间的分配
因为这个关系到链接器后面关于地址计算的步骤,而可执行文件本身的空间分配与链接过程关系不大。

2.3 两步链接

现在的链接器都采用相似段合并的方法,这种方法一般叫做两步链接。
也就是链接过程分两步。

2.3.1 第一步: 空间与地址分配

扫描所有的输入目标文件,并且获得它们的各个段的长度,属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符合引用收集起来,统一放到一个全局符号表。
收集到所有目标文件信息之后,将它们合并,并建立映射关系。

2.3.2 第二步: 符号解析与重定位

使用第一步收集到得所有信息,并且进行符号解析与重定位、调整代码中的地址等。其中,核心步骤是重定位过程。

objdump -h a.o
objdump -h b.o
objdump -h ab

root@ubuntu-admin-a1:/home/4Chapter# objdump -h a.o

a.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
0 .text         0000004f  0000000000000000  0000000000000000  00000040  2**0
				CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data         00000000  0000000000000000  0000000000000000  0000008f  2**0
				CONTENTS, ALLOC, LOAD, DATA
2 .bss          00000000  0000000000000000  0000000000000000  0000008f  2**0
				ALLOC
……
root@ubuntu-admin-a1:/home/4Chapter# objdump -h b.o

b.o:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
0 .text         0000004b  0000000000000000  0000000000000000  00000040  2**0
				CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data         00000004  0000000000000000  0000000000000000  0000008c  2**2
				CONTENTS, ALLOC, LOAD, DATA
2 .bss          00000000  0000000000000000  0000000000000000  00000090  2**0
				ALLOC
……
				
root@ubuntu-admin-a1:/home/4Chapter# objdump -h ab

ab:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
……
13 .text         00000202  0000000000400450  0000000000400450  00000450  2**4
				CONTENTS, ALLOC, LOAD, READONLY, CODE
24 .data         00000014  0000000000601028  0000000000601028  00001028  2**3
				CONTENTS, ALLOC, LOAD, DATA
25 .bss          00000004  000000000060103c  000000000060103c  0000103c  2**0
				ALLOC
26 .comment      00000034  0000000000000000  0000000000000000  0000103c  2**0
				CONTENTS, READONLY
root@ubuntu-admin-a1:/home/4Chapter# 

解释:
由代码中可以看到,VMA(Virtual Memory Address) 即表示虚拟地址
可以看到,链接之前的 a.o b.o的vma都是0,因为虚拟地址空间还没有分配,所以是0;
等到链接之后,可执行文件ab中的各个段都分配到了相应的虚拟地址。

其中,linux下 ELF可执行文件默认从地址 0x08048000开始分配。

2.3.3 符号地址的确定

因为,链接后的虚拟地址已经确定了。
如上面的 .text VMA为 0000000000400450,.data VMA为 0000000000601028。
因为各个符号在段内的相对位置是固定的,所以符号 main iShared swap等的地址已经是确定的。
只不过给每个符号加上一个偏移量,使它们能够调整到正确的虚拟地址。
比如,a.o 的起始地址是0000000000400450
那么 main 的地址是 0000000000400450 + X //X为main的相对偏移量
swap 的地址是 0000000000400450 + Y //X为 swap 的相对偏移量

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值