静态链接详解

引言

上篇文章,我们详细介绍了从源码到可执行程序的全过程,并详细介绍了目标文件的存储格式,ELF格式。这篇文章我们介绍静态链接的全过程,

一、多个目标文件如何合并

多个目标文件在链接过程中,采用相似段合并的方法。比如将所有目标文件的“.text”段合并到输出文件的“.text”段。

二、链接步骤

  1. 扫描所有的输入目标文件,拿到它们各个段的位置、长度和属性;将各个目标文件中的符号表中的符号定义和符号引用统一放到一个全局符号表中。将所有的段合并,重新计算各个段的位置和长度,并建立映射关系。
  2. 进行符号解析与重定位、调整代码中的地址。

三、举例说明链接的全过程

准备a.c 和 b.c 两个文件

a.c

#include <stdio.h>
extern int shared;
int main()
{
    int a = 100;
    swap(&a, &shared);
    return 0;
}

b.c

int shared = 1;
void swap(int* a ,int* b){
    *a ^= *b ^= *a ^= *b;
}
gcc -c a.c b.c  // 只编译不链接,生成a.o b.o
gcc a.o b.o -o ab // 链接生成可执行文件 ab

在这里插入图片描述
在这里插入图片描述

图中的VMA就是虚拟地址,我们关注VMA和Size。可以看到在链接前,目标文件中的VMA都是0,因为还没有分配虚拟空间,默认为0。在链接后,我们使用objdump -h查看ab文件,“.text”段被分配到0x004000e8,大小是0x77字节,“.data”段被分配到0x00601000,大小是0x04字节。链接后的文件及程序虚拟地址如下图所示:

在这里插入图片描述

3.1 符号地址如何确定

经过上一步,输入目标文件的各个段在链接后的虚拟地址就确定了,“.text”段的起始地址为0x004000e8。有了起始地址,各个符号的虚拟地址通过偏移可以计算出来。

四、符号解析与重定位

因为a.c的中变量shared和函数swap都不在本文件中,所以这两个符号就需要重定位。在编译a.o目标文件的时候,因为编译器不知道这两个符号的地址,会先用一个虚假值代替,在链接的时候通过重定位进行地址修正。

重定位表

链接器就通过重定位表知道哪些符号需要重定位。每个需要重定位的段,都会有一个对应的重定位表。比如: “rela.text” 就是针对 “.text”段的重定位表,这是因为“.text”段中的“swap” 函数的调用和shared变量的引用;

可以通过:objdump -r 查看目标文件的重定位表。
在这里插入图片描述
每个需要重定位的地方都有一个重定位入口,从图中我们可以看到a.o有两个重定位的入口。“RELOCATION RECORDS FOR [.text]” 表示这个重定位表是代码段的重定位表。图中的OFFSET 0x14 和 0x21 分别对应通过a.o的反汇编结果中需要重定位的地方。
在这里插入图片描述

符号解析

如果只连接a.o,链接器就会找不到shared和sawp的定义而报错:undefined reference to “shared”和“swap”。就是链接时符号未定义。
在这里插入图片描述
在这里插入图片描述

链接时,链接器通过查找重定位表发现shared和swap符号需要重定位,就会查找所有目标文件建立的全局符号表,若是没找到就报未定义错误。

文章参考于<零声教育>的C/C++linux服务期高级架构,及书籍(程序员的自我修养)。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《gcc链接脚本详解.pdf》是一篇介绍GCC链接脚本的文章。GCC链接脚本是用于指定目标文件之间如何链接的文件。对于大型项目而言,链接脚本的编写和使用是非常重要的。 文章首先介绍了链接脚本的作用和基本结构。链接脚本的主要作用是指定应该如何将目标文件链接在一起。链接脚本的基本结构包括:脚本头部、段定义和符号定义。 接下来,文章详细介绍了链接脚本的各个部分。段定义用于指定目标文件中各个段在内存中的位置以及如何对齐。符号定义用于指定如何链接各个目标文件中的符号。通过对这些定义的灵活使用,可以实现各种复杂的链接需求。 此外,文章还介绍了如何在链接脚本中使用表达式,以及如何使用链接脚本进行“分离链接”和“交叉链接”。这些高级特性可以应对一些特殊的链接场景,是GCC链接脚本的重要应用。 总的来说,文章对GCC链接脚本的作用、结构和使用都进行了详细的阐述,对于需要在开发过程中进行链接脚本编写和优化的开发者来说,是一篇非常有价值的文章。 ### 回答2: 《gcc链接脚本详解.pdf》一文详细介绍了GCC链接器的工作原理以及如何使用链接脚本进行自定义的链接操作。其中,PDF文档中首先介绍了链接器的工作流程,包括输入文件的读取、符号解析、重定位等步骤。接着介绍了链接脚本的作用和使用方法,给出了几个示例以帮助读者更好的理解。 对于GNU链接器而言,链接脚本是非常重要的一部分,因为它可以让程序员对链接过程进行更为细致的控制,实现一些特定的功能。例如,可以通过链接脚本来定义程序的内存布局、修改符号表、控制跨文件静态变量的分配等。链接脚本使用灵活,但难度较大,需对链接器和可执行文件的结构有一定的了解。 这份PDF文档中详细介绍了链接脚本的语法和参数含义,以及如何在命令行中使用链接脚本。另外,在文档末尾,作者还提供了一些链接脚本的例子,帮助读者更好的理解如何编写链接脚本。 总之,《gcc链接脚本详解.pdf》是一份非常有价值的文档,适合那些对链接器和链接脚本有一定了解基础,并希望深入研究和优化链接器性能的程序员学习。 ### 回答3: 《gcc链接脚本详解.pdf》这篇文章主要介绍了gcc的链接脚本,解释了链接脚本的基本原理和语法格式,并给出了一些实例和应用场景。 在编译链接过程中,链接器需要确定程序的地址空间布局,即各个段(如代码段、数据段、堆栈段)在内存中的位置和大小。链接脚本就是用来描述这个布局的文件。链接脚本会指定各个段在内存中的地址范围、对齐方式、内存属性和是否需要在程序中加入。通过链接脚本,程序员可以灵活地控制程序的内存布局和各个段的属性。 本文详细讲解了链接脚本的语法格式,包括源文件、输出文件、段定义、符号定义、输出格式等部分。同时,还给出了一些示例,如如何将某个段放在特定的地址范围内、如何合并两个段、如何设置默认的内存对齐方式等。这些示例很好地展现了链接脚本的实际应用场景。 此外,文章还介绍了一些高级的应用,如静态链接库的链接脚本。这部分文章通俗易懂地解释了静态链接库的链接过程,并演示了如何使用链接脚本实现静态链接库的版本控制。 总的来说,《gcc链接脚本详解.pdf》这篇文章详细介绍了gcc链接脚本的语法和应用场景,对于想深入了解链接过程的程序员非常有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值