深入理解计算机系统——链接(一)

        链接(linking)是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时(compiletime),也就是在源代码被翻译成机器代码时;也可以执行于加载时(loadtime),也就是在程序被加载器(load-er)加载到内存并执行时;甚至执行于运行时(run time),也就是由应用程序来执行。在早期的计算机系统中,链接是手动执行的。在现代系统中,链接是由叫做链接器(linker)的程序自动执行的

        链接器在软件开发中扮演着一个关键的角色,因为它们使得分离编译(separatecompilation成为可能。不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。

        ① 理解链接器将帮助你构造大型程序。构造大型程序的程序员经常会遇到由于缺少模块、缺少库或者不兼容的库版本引起的链接器错误。
        ② 理解链接器将帮助你避免一些危险的编程错误。Linux 链接器解析符号引用时所做的决定可以不动声色地影响你程序的正确性。在默认情况下,错误地定义多个全局变量的程序将通过链接器,而不产生任何警告信息。

        ③ 理解链接将帮助你理解语言的作用域规则是如何实现的。例如,全局和局部变量之间的区别是什么?当你定义一个具有 static 属性的变量或者函数时,实际到底意味着什么?

        ④ 理解链接将帮助你理解其他重要的系统概念。链接器产生的可执行目标文件在重要的系统功能中扮演着关键角色,比如加载和运行程序,虚拟内存,分页,内存映射

        ⑤ 理解链接将使你能够利用共享库。随着共享库和动态链接在现代操作系统中重要性的日益加强,链接成为一个复杂的过程,为掌握它的程序员提供了强大的能力。比如,许多软件产品在运行时使用共享库来升级压缩包装的(shrink-wrapped)二进制程序。

这一章提供了关于链接各方面的全面讨论,从传统静态链接到加载时的共享库的动态链接,以及到运行时的共享库的动态链接。将使用实际示例来描述基本的机制,而且指出链接问题在哪些情况中会影响程序的性能和正确性。为了使描述具体和便于理解,我们的讨论是基于这样的环境:一个运行 Linux 的 x86-64 系统,使用标准的ELF-64(此后称为 ELF)目标文件格式。

7.1 编译器驱动程序

        考虑图7-1中的C语言程序。它将作为贯穿本章的一个小的运行示例,帮助我们说明关于链接是如何工作的一些重要知识点。

        大多数编译系统提供编译器驱动程序,它代表用户在需要时调用语言预处理器编译器汇编器和链接器。比如,要用 GNU 编译系统构造示例程序,就要通过在 shell 中输入下列命令来调用 GCC 驱动程序:

linux> gcc -Og -o prog main.c sum.c

        图7-2 概括了驱动程序在将示例程序从 ASCII码 源文件翻译成可执行目标文件时的行为。(如果想看这些步骤,用 -v 选项来运行 GCC。)驱动程序首先运行 C 预处理器(cpp),它将C的源程序 main.c 翻译成一个 ASCII 码 的中间文件 main.i:

cpp [other arguments] main.c  /tmp/main.i

接下来,驱动器运行C编译器(ccl),将 main.i 翻译成一个 ASCII 汇编语言文件 main.s:

 ccl /tmp/main.i -Og [other arguments] -o /tmp/main.s

 然后,驱动程序运行汇编器(as),将 main.s 翻译成一个可重定位目标文件(relo-catable object file)main.o

as [other arguments] -o /tmp/main.o /tmp/main.s

驱动程序经过相同的过程生成 sum.o。最后,它运行链接器程序 ld将 main.o 和 sum.o 以及一些必要的系统目标文件组合起来,创建一个可执行目标文件(executable object file) prog:

ld -o prog [system object files and args]        /tmp/main.o /tmp/sum.o 

要运行可执行文件 prog,在Linux shell 的命令行上输入名字:

 linux> ./prog

 shell 调用操作系统中一个叫做加载器(loader)的函数,它将可执行文件 prog 中的代码和数据复制到内存,然后将控制转移到这个程序的开头

7.2 静态链接

         像 linux LD 程序这样的静态链接器(static linker)一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的、可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件各种不同的代码 和 数据节(section) 组成每一节都是一个连续的字节序列。指令在一节中,初始化了的全局变量在另一节中,而未初始化的变量又在另外一节中。

         为了构造可执行文件,链接器必须完成两个主要任务:

        ① 符号解析(symbolresolution)。目标文件 定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量(即C语言中任何以static属性声明的变量)。符号解析的目的是将每个符号引用正好和一个符号定义关联起来

        ② 重定位(relocation)。编译器 汇编器 生成 从地址 0 开始的代码和数据节链接器通过把每个符号定义与一个内存位置关联起来从而重定位这些 节 ,然后修改所有对这些符号的引用,使得它们指向这个内存位置。链接器使用汇编器产生的重定位条目的详细指令不加甄别地执行这样的重定位

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值