【深入理解计算机系统笔记】Linux 下 程序的链接过程

我们第一个hello程序的生命周期是从一个人工编辑的文本文件hello.c 开始的,然后经过翻译形成一个可执行文件,这之后通过加载器将可执行目标文件的代码和数据从磁盘复制到内存中,然后通过跳转到程序的第一条指令或入口点来运行该程序。
其中翻译过程又可以分为预处理,编译,汇编,链接这四个步骤。如下图所示:
这里写图片描述
预处理阶段根据以#字符开头来读取系统头文件,把它直接插入到程序文本中,得到 hello.i 文本文件,文本文件是用ASCII编码的。
编译阶段将文本文件hello.i 翻译成文本文件hello.s ,它包含一个汇编语言的程序。
汇编阶段将hello.s翻译成机器语言指令。把这些指令打包成一个可重定位的目标程序hello.o,它是一个二进制文件。
链接阶段。阅读了第七章,主要对链接阶段详细说明。

链接阶段

链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个形成的文件可以被加载到内存中运行。链接可分为编译时链接,加载时链接,运行时链接。前两种是静态链接,后两种是动态链接。我个人对链接的理解就是将各种代码和数据“分类整理”,确定无误后分配运行时地址,合并成一个文件。
静态链接有两个任务:符号解析和重定位。

符号解析

目标文件中有定义的符号,有引用的符号,每一个符号可能是一个函数,一个全局变量,或者是一个static声明的静态变量。符号解析的目的就是将每个符号的引用正好和一个符号的定义关联起来。
先来看看可重定位目标文件的组成: 可重定位目标文件由ELF 头节头部表以及他们中间的节构成,这些节中有:

  • .text : 已经编译程序的机器代码;
  • .rodata: 只读数据,比如printf中的格式串和开关语句的跳转表;
  • . data: 已初始化的全局和静态变量。局部变量是在运行时被保存在栈中。不在可重定位目标文件中出现;
  • . bss: 未初始化的全局和静态变量,以及所有被初始化未0 的全局和静态变量。这个节不占据实际的空间,只是一个占位符。划分已初始化和未初始化的变量是为了空间效率:没有初始化的变量就没有必要占据任何实际的磁盘空间。运行时,在内存中分配这些变量,初始值为0
  • . symtab: 一个符号表,存放在程序中定义和引用的函数和全局变量信息。每个可重定位目标文件在 .symtab 中都有一个符号表。和编译器的符号表不同, .symtab 符号不包括局部变量的条目。
    有三种不同的符号:
    1)由本模块定义的并能够被其他模块引用的全局符号——非静态函数和全局变量
    2)由其他模块定义并能被本模块引用的全局符号,称为外部符号——对应于其他模块定义的非静态函数和全局变量
    3) 只能被本模块定义和引用的局部符号——带static属性的函数和全局C变量。
    编译器只允许每个模块中每个局部符号有一个定义,静态局部变量也会有本地链接器符号,编译器还要保证他们有唯一的名字。对于不是在昂前模块中定义的符号,会假设该符号是在其他某个模块中定义的,生成一个链接器符号条目,并把它交给链接器处理。当链接器在其他输入模块中都找不到这个被引用的符号的定义,就输出一条错误信息终止。
    还有其他的节,不做介绍。
重定位

一旦链接器完成了符号解析,就把代码中每个符号引用和正好一个符号的定义关联起来。就开始了重定位步骤,在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。
重定位由两部分构成:重定位节和符号定义;重定位节中的符号引用。

静态链接和动态链接

首先要了解静态库和动态库:

  • 静态库
    一般为 .a文件,作为链接器的输入。比如printf函数需要引用一个目标模块,通常将这些模块提前打包,链接器就把这个目标模块复制进程序中。利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即链接后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被复制进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
  • 动态库(共享库)
    一般为 .so文件, 相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。
静态链接

链接器将函数的代码从其所在地(目标文件或静态链接库中)拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

动态链接

在此种方式下,函数的定义在动态链接库或共享对象的目标文件中。在编译的链接阶段,动态链接库只提供符号表和其他少量信息用于保证所有符号引用都有定义,保证编译顺利通过。动态链接器(ld-Linux.so)链接程序在运行过程中根据记录的共享对象的符号定义来动态加载共享库,然后完成重定位。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值