静态链接

    当我们有两个目标文件时,如何将它们链接起来形成一个可执行文件?使用以下两个源代码文件作为例子展开分析: 
// 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;
}
  
    分别编译成目标文件 a.o 和 b.o,我们接下来要做的是将这两个目标文件链接在一起并最终形成一个可执行文件 ab
    将相同性质的段合并到一起,代码段、数据段、bss段等,整个链接过程分两部分:
  • 第一步 空间与地址分配 扫描所有的输入目标文件,获得它们的各个段的长度、属性和位置,并且将输入目标文件中的符号表中的所有符号定义和符号引用收集起来,统一放到一个全局符号表。这一步中,链接器将能够获得的所有输入目标文件的段长度,并且将它们合并,计算输出文件中各个段合并后的长度和位置,并建立映射关系。
  • 第二步 符号解析与重定位 使用第一步收集到的所有信息,读取输入文件中段的数据、重定位信息,并且进行符号解析与重定位、调整代码中的地址等。
    使用链接器将两个目标文件链接起来: $ ld a.o b.o -e main -o ab
    查看a.o b.o ab的段属性及ab的字符串表:




    我们知道main函数位于a.o的代码段,其代码段长度为00000027,4字节对齐;则可以推断出main函数位于ab中代码段的起始位置08048094,swap函数在ab中的位置就是ab代码段的起始位置 + a.o中代码段长度(00000028, 4byte对齐) = 080480bc。shared在b.o中数据段的起始位置,我们推断shared在ab中数据段的起始位置08049154
    
    在完成空间和地址的分配步骤以后,链接器就进入了符号解析与重定位的步骤。使用“objdump -d”查看a.o的代码段反汇编结果:

    
    源代码a.c被编译成目标文件时,编译器并不知道“shared”和“swap”的地址,所以编译器暂时把地址0看作是“shared”地址,fffffffc看作是“swap”的地址。查看ab的代码段反汇编结果:

    我们可以看到main函数的两个重定位入口被修正到正确的位置。
 
    链接器如何晓得的呢?我们知道对于可重定位的ELF文件来说,它必须包含可重定位表,我们使用“objdump -r”查看a.o的重定位表:

对照a.o的反汇编我们知道00000015和00000021分别就是a.o代码段需要重新调整的地方。
    其实,重定位过程也伴随着符号的解析过程,每个目标文件都可能定义一些符号,也可能引用到定义在其他目标文件的符号。重定位的过程中,每个重定位的入口都是对一个符号的引用,那么当链接器须要对某个符号的引用进行重定位时,它就要确定这个符号的目标地址。这时候链接器就回去查找由所有目标文件的符号表组成的全局符号表,找到相应的符号进行重定位。我们查看a.o的符号表:

    “GLOBAL”类型的符号,除了main函数是定义在代码段以外,其他两个“shared”和“swap”都是“UND”,即未定义类型,这种未定义的符号都是因为该目标文件中有关于它们的重定位项。所以在链接器扫描完所有的输入目标文件之后,所有这些未定义的符号都应该能够在全局符号表中找到,否则链接器就报符号未定义错误。

    x86基本重定位有两种方式:
    我们根据a.o的重定位表定位a.o的重定位入口:

    shared在.text段的偏移为00000015,重定位类型为R_386_32,则由a.o的反汇编知晓00000015偏移位置的修正值为0,而shared的实际位置为08049154,则将a.o代码段映射到ab代码段后偏移为00000015位置的值修正为08049154 + 00000000 = 08049154;
swap在.text段的偏移位置为00000021,重定位类型为R_386_PC32,则由a.o的反汇编知晓00000021偏移位置的修正值为-4(fffffffc),而swap的实际位置为080480bc,则将a.o代码段映射到ab后偏移为00000021位置的值修正为080480bc - 00000004 - (08048094+00000021)=00000003
     COMMON块
    我们分析下多个符号定义类型不一致的几种情况:
  • 两个或两个以上的强符号类型不一致;
  • 有一个强符号,其他都是弱符号,出现类型不一致;
  • 两个或两个以上弱符号类型不一致
对于上述情况,第一种情况无须处理,属非法错误,链接器回报符号多重定义错误。
采用COMMON块机制,编译器将未初始化的全局变量定义作为弱符号处理,如果有一个符号为强符号,那么最终输出结果中的符号所占的空间与强符号相同,若链接过程中有弱符号空间大于强符号空间,链接器回报警告信息。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值