链接器(linker)的作用——CSAPP第7章读书笔记

  首先说说我为什么要去读这一章。这个学期开OS的课,在Morden Operating System上读到和Process有关的内容时看到这样一句话:“Process is fundamentally a container that holds all the information needed to run a program.”当时瞬间就想到了之前在csapp上看的模棱两可的“目标可执行文件”这个概念,于是重新又把它的第7章给读了一遍。



  要理解linker的作用,首先要搞明白他在整个计算机系统中处于一个什么样的位置。

  关于一个程序是怎样从码农们手撕的代码变成内存中能跑起来的程序这个过程就不再过多的叙述,这篇文章只是着重的去讲一下有关linker的这一部分。我们可以看到,linker的接受的输入是若干个.o文件,简单的说就是经过汇编器编译后生成的机器码,学名叫“relocatable object file(可重定位的目标文件)”,概念相近的称呼也有“module(模块)”。而汇编器的输出,是一个名叫“executable object program(目标可执行文件)”的二进制文件,这个文件的特征就是可以直接拷贝到内存中不需做任何的更改便可以运行。那么我们研究linker的作用是什么就可以从这里入手——为了构造最终的目标可执行文件,他需要对输入若干可重定位的目标文件做哪些事情?

  linker的作用主要有两个:

  (1)符号解析(symbol resolution):将每个符号的定义和每个符号的引用联系起来。(就是让系统明白,当这个程序run的时候,遇到的具体的变量或函数名,他们到底来自哪个文件的定义?是自己这个?还是其他一起输入linker的文件?)

  (2)重定位(relocation):把每个符号定义与存储器中的一个具体位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器的位置,从而重定位这些节。(在取得了每个符号的引用和定义的连接之后,要把符号的定义在存储器中绑定一个具体的地址)

  书中对符号的解释不是太清楚,至少我一开始的时候没太理解这个概念,在这儿结合书本的内容我用自己的话来概括下我对这个概念的理解。“符号”可以分为3类:

  1、由该模块定义的并且能被其他模块引用的“全局符号”。这里的“全局符号”对应于C语言中的非静态的函数和全局变量。

  2、由其他模块定义的由该模块引用的“全局符号”。解释同上

  3、由该模块定义的并且不能被其他模块引用的“全局符号”。对应于C语言中的静态变量,即static变量。static关键字相当于C语言中的“private”,即只能被自己这个文件(模块)使用的全局变量

  应当注意的是这里的变量全是全局变量而不是函数内部的私有变量,私有变量由运行时stack存储管理,linker对她并不感兴趣:)



  那么在了解了符号的概念之后,要想具体的了解linker对可重定位的目标可执行文件做的一些事情,就要了解relocatable object file的一些结构(他是怎么记录自身的各种符号信息的?)对不对?

  大家第一次看到这个图不要害怕,其实这就是汇编器(Assembler)将编译器处理的源代码文件进行进一步的编译或者说汇编之后形成的可重定位的目标可执行文件。这个文件的一个个小格子就是一个个的“节(section)”,他们存放该program的各种信息,在这里我只会解释几个我认为对理解linker作用很有必要的section。

.text:已编译程序的机器代码。

.data:已初始化的全局C变量。

.bss:未初始化的全局C变量。在这里符号只是一个占位符,它不占用任何的内存空间。

.symtab:一个符号表,存放在程序中定义和引用的函数和全部全局变量的信息。

.rel.test:存放代码的重定位条目(relocation entry)。

.rel.data:存放数据的重定位条目。



  以上都属于本章的基础知识铺垫部分,理解了上述内容,就可以很容易的理解linker对可重定位的目标可执行文件所做的操作了。

1、符号解析

  linker解析符号的方法是将每个符号的引用与所有输入的relocatable object file中的.symtab节中所有的符号定义中确定的一个联系起来。

 1.1链接器如何解释多重定义的全局符号?

  对于定义和引用都在一个module中的符号,linker的操作很简单,不需要指来指去改来改去;而真正要深入探讨的操作是对定义和引用不在同一个文件中的符号,尤其是当寻找到的符号定义有重名时。对此linker的做法是:

  (1)定义强符号和弱符号的概念。函数和已初始化的变量是强符号,为初始化的变量是弱符号。

  (2)设定规则。当有多重定义冲突的时候,linker遵循的规则是:

    one:不允许有多个强符号定义

    two:如果有一个强符号和多个弱符号定义,那么选择强符号定义

    three:如果有多个弱符号定义,那么随便选择一个

 1.2与静态库链接

  为什么会有“静态库”(static libraries)这个概念?

  首先在C语言编程中,我们需要实现丰富的功能,就要使用各种各样的函数接口。以ANSI C为例,它定义了一组广泛的标准I/O、字符串操作和整数数学函数,例如atoi、printf、scanf、strcpy、rand。他们在libc.a库中,对每个C程序来说都是可用的。如果不使用静态库,我们看看编程开发人员可以用什么其他的办法来向用户提供这些函数。

  一种实现的方法是让编译器直接辨认出对函数的调用,并直接生成相应的代码——这显然是不可行的,C语言中有大量的函数,这样做显然会使得编译器的设计变得相当复杂,每次添加、修改、删除一个函数时,都需要一个新的编译器版本。虽然对于编程人员而言这样是十分方便的,因为所有的标准函数都是直接可用的。

  另一种实现的方法是将所有的这些函数放到一个单独的可重定位的目标可执行文件中,它的优点是将编译器的设计与标准函数的实现分离开来,在一定程度上仍然便利编程人员。但是这样做的缺点却是每次运行程序的时候都要将该装载函数的rof文件copy到内存中去,而这样是很浪费内存空间的。而且同样将这么一大批函数赛到一个文件中,每次的维护都要重现编译整个源文件,这又是相当大的一个工作量。

  何为静态库?

  在Unix中,静态库以archive这种特殊的文件格式存在于磁盘中,是一组连接起来的relocatable object file的集合。

  

 1.3链接器如何使用静态库来解析引用

  维护一个基于(U,E,D)三个集合的算法



2、重定位

  在这个过程中,将合并模块并为每个符号分配运行时的地址。重定位由两个步骤组成:

  在这里有一个需要理解的概念是重定位条目(relocation entry)。在汇编器生成一个可重定位的目标模块时,当遇到UNDEFINED的符号,即不知道该数据或代码最终该存放到存储器的什么位置时,它就会为该符号生成一个重定位条目,即之前介绍的可重定位目标文件中的.rel.text和.rel.data两个表所记录的内容。

时间: 2024-09-29 09:26:52

链接器(linker)的作用——CSAPP第7章读书笔记的相关文章

4.链接器脚本

4.链接器脚本 一.体验: 首先先看一个例子:图1-1: 编译烧写的过程: main.c的截图: 可以看到在main函数中点亮了第一个盏灯: 图1-1 点灯 在这个例子中的gboot.lds的代码为: OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { ????. = 0x50008000; ???? ????. = ALIGN(4); ????.text : ????{ ????start.o (.text) //.text指明的是代码段,代码段里指定第一个执行

软件开发--链接器

一.链接器上 链接器的意义--链接器的主要作用是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确的衔接A.目标文件的秘密1.各个段没有具体的起始地址,只有段大小信息2.各个标识符没有实际地址,只有段中的相对地址3.段和标识符的实际地址需要链接器具体确定链接器的工作内容--将目标文件和库文件合为最终的可执行程序1.合并各个目标文件中的段(.text .data .bss)2.确定各个段和段中标识符的最终地址(重定位)运行的示例根据之前一篇博客中的开发中的工具可以知道,同时使用nm命令可

解释器编译器区别,链接器作用。

编译器是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快; 而解释器则是只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是不如编译后的程序运行的快的.这是因为计算机不能直接认识并执行我们写的语句,它只能认识机器语言(是二进制的形式) 链接器作用: 一个大的程序往往会分为多个源程序文件来编写,因而需要对多个源程序分别进行编译或汇编,来生成多个不同的目标文件(.o文件),这些目标文件包含指令.数据.和其他说明信息

C编译器、链接器、加载器详解

摘自http://blog.csdn.net/zzxian/article/details/16820035 C编译器.链接器.加载器详解 一.概述 C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接是把目标文件.操作系统的启动代码和用到的库文件进行组织形成最终生成可加载.可执行代码的过程. 过程图解如下: 预处理器:将.c 文件转化成 .i文件,使用的gcc命令是

链接器相关问题

从.c文件到 可执行 文件需要经历的过程 static linking 静态链接器的输入是一组可重定向文件,输出一个全链接的可执行目标文件. 目标文件有三种形式: 1. 可重定向目标文件:包含二进制代码和数据,可以在编译时和其他可重定向目标文件一起,得到可执行文件. 2. 可执行目标文件:包含二进制代码和数据,可以直接拷贝进内存进行执行. 3. 共享目标文件:一种特殊的重定向文件,可以被加载进内存,并在加载和运行期间进行动态链接. 编译器和汇编器产生可重定向目标文件(包括共享目标文件) 链接器产

018链接器脚本

1.链接器脚本的神奇作用 通过使用不同的链接器脚本,不需要更改源代码,就可以实现不同的功能! 链接器脚本对程序的生成有着至关重要的作用! 2.链接器脚本--->段 一个可执行程序通常是由:代码段,数据段,bss段构成的. 同样,在用于链接这个程序的链接器脚本中,就会反映出这几个段的信息. 段的信息是由SECTIONS固定的标号指明链接器脚本最重要的信息是段: 代码段里面包含的是所有文件的代码: 数据段包含的是所有文件的数据: 最基本的框架如下: 3.链接器脚本--->设置起始链接地址 . =

[From 2.4]C#编译器和程序集链接器(以及一些它们的命令开关)

C#编译器:(用csc命令来使用C#编译器) 程序集链接器(Assembly Linker):(用al命令来使用C#编译器) 除了使用C#编译器,还可以使用"程序集链接器"使用程序AL.exe来创建程序. 1.如果程序集要包含由不同编译器生成的模块(而且这些编译器不支持与C#编译器的/addmodule). 2.可用AL.exe生成只含资源的程序集,也就是所谓的附属程序集(satellite assembly),它们通常用于本地化.(例子3) 3.能生成EXE文件,或者生成只包含清单(

链接器link.exe 编译器cl.exe 资源编译器rc.exe

原文地址:https://blog.csdn.net/biggbang/article/details/24433065 1.cl.exe文件是Visual C\C++的编译器,它将程序源代码文件编译为obj文件. 2.rc.exe文件是资源编译器.工程项目中的.rc文件中包含了对程序中所使用资源(菜单.图标等)的描述.rc.exe将.rc格式的文件编译为.res文件,供链接器链接到可执行文件中. 3.link.exe是Windows平台的链接器,它将cl.exe编译生成的obj文件,资源编译器

浅谈C++编译原理 ------ C++编译器与链接器工作原理

原文:https://blog.csdn.net/zyh821351004/article/details/46425823 第一篇:     首先是预编译,这一步可以粗略的认为只做了一件事情,那就是“宏展开”,也就是对那些#***的命令的一种展开. 例如define MAX 1000就是建立起MAX和1000之间的对等关系,好在编译阶段进行替换. 例如ifdef/ifndef就是从一个文件中有选择性的挑出一些符合条件的代码来交给下一步的编译阶段来处理.这里面最复杂的莫过于include了,其实