静态链接

之前一直都不清楚链接的作用,最近正好在做一个应用移植,需要修改工具链,趁此机会初步明白了链接的概念。下面结合俞甲子的《程序员的自我修养——链接、装载与库》简单的介绍一下静态链接的过程。

从我们所写的所代码到可执行文件,这期间都发生了什么?

在Linux下编程的人应该都清楚,编写好源代码需要经过gcc编译形成目标文件,ld链接目标文件成为可执行文件。编译过程在这里不讨论,简单的讲就是经过语法分析、词法分析、语义分析,根据处理器所支持的指令集,将源代码转变成机器码,也就是目标文件(.o文件)。

如果程序源代码含有多个源文件,那么编译器对每个源文件进行单独编译,从而形成多个目标文件。源文件之间一般是存在着引用的关系的,编译之后为了满足源文件之间的引用关系,需要用链接器(ld)将多个目标文件链接在一起,同时源文件中如果有引用的库函数的话,还要将相应的库也链接进来,形成完整的可执行程序。

具体的解释这个过程就要从编译器对每个源文件进行单独编译开始说了,比如我们有以下两个源文件 a.c 与 b.c, 代码如下所示。

/*  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.c 和b.c 进行单独编译形成 a.o 与b.o,这时注意到文件a.c中的变量shared及函数swap都是在文件b.c中定义的,因此在a.o中并不知道shared与swap的确切值及地址,此时就需要链接器来帮忙了,链接器的作用主要有两方面:一是空间与地址分配,二是符号解析与重定位。

链接操作: $ld a.o b.o -o main
第一步,空间与地址分配,简单讲就是将所有的输入文件合并起来,合并的方法是将所有文件相同性质的段合并在一起,比如将所有输入文件的“.text”(代码段)合并到输出文件的“.text”段等。

第二步,符号解析与重定位。在完成空间和地址的分配步骤后,链接器就进入到了符号解析与重定位的步骤,这是静态链接的核心内容。

首先看一下a.o里面包含两个外部符号“shared”“swap”,在编译时并不知道这两个符号的地址,于是编译器就暂时把地址0看成是“shared”的地址,同理“swap”的地址也是一个虚假地址。链接器在完成第一步空间和地址分配时就已经可以确定所有符号的地址了,所以链接器可以根据符号的地址对之前的虚假地址进行修正,这就是重定位。

符号解析是链接器通过查看文件的符号表完成的,每个文件都包含一个符号表,表中包括所有的局部变量和全局变量。链接器本身会保存一个全局符号表,这样每当链接器读入一个输入文件,它会将该文件中所有的全局符号加入到自己的全局符号表中,并将定义或引用每个全局符号的位置用链表组织起来。通过查询索引可以查找到所有的未定义的符号,否则就会出现“undefined reference to XXX”错误。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值