《程序员的自我修养》第四章学习笔记
2015.12.26的笔记,放在了草稿箱。2023.8.24发布一下吧。
第四章 静态链接
先上两个文件
// a.c extern int shared; 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 b.c
最后用ld链接器进行链接
$ ld a.o b.o -e main -o ab // -e main 表示将main函数作为程序入口
这样 就一共有了三个目标文件 a.o b.o ab
4.1 空间与地址分配
a.o b.o 链接生成 ab。 对于多个输入目标文件,链接器如何将它们的各个段合并到输出文件。
4.1.1 按序叠加:简单的将各个目标文件文件依次合并。 造成空间浪费(因为每个段都需要有一定的地址和空间对齐要求)。
4.1.2 相似段合并:将相同性质的段合并到一起。 a.o的.text 和 b.o的.text 合并成 ab的.text。其他的段也类似。
1,“链接器为目标文件分配地址和空间”:“地址和空间”有两层含义,一是在输出的可执行文件中的空间。二是在装载后的虚拟地址中的虚拟地址空间。
2,链接器空间分配的策略采用一种叫两步链接的方法(分成两步)
(1)第一步 空间与地址分配:扫描所有输入目标文件,获取它们的各个段的长度、属性和位置,将输入目标文件中的符号表中所有的符号定义和符号引用搜集起来,同意放到一个全局符号表中。
这一步中链接器将能够获得所有输入目标文件的段长度,并且将它们合并,计算输出文件各个段合并后的长度和位置,并建立映射关系。
(2)第二步符号解析和重定位: 使用上面搜集到的信息,读取输入文件段中的数据、重定位信息,并且进行符号解析与重定位、调整代码中的地址。
3,$ ld a.o b.o –e main –o ab -e main 表示将main函数作为程序入口。ld链接器默认的程序入口为 _start。
4,在Linux下,ELF可执行文件默认从地址0x08048000开始分配。
5,用objdump来查看链接前后地址的分配情况
$ objdump -h a.o
$ objdump -h b.o
$ objdump -h ab
File off:文件偏移
Size:大小
VMA:Virtual Memory Address 虚拟地址(我们主要关心这个和Size)
LMA:Load Memory Address 加载地址
正常情况下 LMA VMA应该是一样的,但是在有些嵌入式系统中,这两个有可能是不一样的。
说明:
1)在链接之前,目标文件的VMA都是0,因为虚拟空间还没有被分配。链接之后,可执行文件ab中的各个段都被分配到了相应的虚拟地址。
2)在Linux下,ELF可执行文件默认从地址0x08048000开始分配。
4.1.3 符号地址的确定