《深入理解计算机系统》课本自学笔记

第七章 链接

By20135203齐岳

链接:将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或拷贝)到存储器并执行。

现代计算机中链接由链接器自动完成。

链接器在软件开发中的重要功能:分离编译。

编译器驱动程序

大多数编译系统提供编译驱动程序,它代表用户在需要调用语言处理器、编译器、汇编器和链接器

例如,使用GNU编译系统构造示例程序:

gcc -02 -g -o p main.c swap.c具体步骤如下:
#运行预处理器cpp生成ASCII码的中间文件main.i
cpp [other arguments] main.c /tmp/main.i
#运行C编译器cc1,将mian.i翻译成一个ASCII汇编语言文件main.s
cc1 /tmp/main.i main.c -02 [other arguments] -o /tmp/main.s
#运行汇编器as,将mian.s翻译成一个可重定位目标文件main.o
as [other arguments] -o main.o /tpm/main.s
#运行链接器程序ld,将mian.o和swap.o以及一些必要的系统目标文件组合起来,创建一个可执行目标文件p
ld -o p [system object files and args] /tmp/main.o /tmp/swap.o
#要运行可执行文件p,直接在shell上输入它的名字
./p

外壳调用操作系统中的加载器,它拷贝可执行文件p中的代码和数据到存储器,然后控制转移到这个程序的开头。

静态链接

静态链接器

以一组可重定位的目标文件和命令行参数作为输入,生成一个完全连接的可以加载和运行的可执行目标文件作为输出。

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

  • 符号解析:目标文件定义和引用符号。将每个符号引用刚好和一个符号定义联系起来。

  • 重定位:编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器的位置。

    目标文件

  • 可重定位文件:保存代码和适当的数据,用来和其他object文件一起创建一个可执行文件或一个共享文件。主要是.o文件。

  • 可执行文件:保存一个用来执行的程序,指出了exec(BA_OS)如何来创建程序进程映象,怎么把文件加载出来以及从哪里开始执行。

  • 共享文件:保存着代码和数据用来被以下两个链接器链接。一是链接编译器,可以和其他的可重定位和共享文件创建其他的object文件;二是动态链接器,联合一个可执行文件和其他 共享文件来创建一个进程映象。主要是.so文件。

可重定位目标文件

ELF是目标文件的格式:

ELF头

16字节,包含系统的字的大小和字节顺序,帮助链接器语法分析和解释目标文件的信息(ELF头的大小,目标文件的类型-可重定位-共享-可执行,机器类型-IA32,节头部表的文件偏移,节头部表中的条目大小和数量)。

节头部表

描述不同节的位置和大小。节就是夹在ELF头和节头部表之间的。节头部表包含很多条目,每一个条目的大小都是一样的,每一个条目都对应一个节。

  • .text——包含已编译的机器代码。
  • .rodata—只读数据(printf中的格式串等)
  • .data——已初始化的全局C变量。
  • .bss——未初始化的全局C变量。(只是一个占位符,不占据空间)
  • .symtab——一个符号表,包含:程序定义引用的函数和全局变量。
  • .rel.text——服务于.text节,是描述.text中位置的列表。当链接器把本目标文件和其他文件结合时,这个小节,就要修改。
  • .rel.data——服务于.data,被模块引用或定义的任何全局变量的重定位信息。同上,和其他文件结合时,这个小节,就要修改。
  • .debug——一个调试符号表,表中记录着程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的c源文件。只有以-g选项调用编译驱动程序时才会得到这张表。
  • .line——原始c源程序中的行号和.text节中机器指令之间的映射。同上-g才有。
  • .strtab——一个字符串表,其中包含.symtab和.debug节中的符号表,以及节头部表中的节名字。字符串表,就是一个一个的字符串,字符串就是以NULL结尾的字符序列。

符号和符号表

每个可重定位目标模块m都有一个符号表,包含m所定义和引用的符号的信息。在链接器的上下文中有三种不同的符号:

  • 由m定义并能被其他模块引用的全局符号。全局链接器符号对应于非静态的C函数和不带C static属性的全局变量。

  • 由其他模块定义并被模块m引用的全局符号。称为外部符号。对应于定义在其他模块中的C函数和变量。

  • 只被模块m定义和引用的本地符号。称为本地链接器符号。对应于带static属性的C函数和全局变量。

符号表是一个数组,数组的元素是条目,条目的格式是固定的,如下图所示:

  • name是字符串表中的字节偏移,指向符号的以null结尾的字符串名字。
  • value是符号的地址,对于可重定位模块来说,这是一个相对的偏移地址;对于可执行目标文件来说,是一个绝对运行时的地址。
  • size是目标的大小(字节为单位)。
  • type为数据或函数,表示符号是数据还是函数还是节还是路径名。
  • binding表示是全局的还是本地的。
  • reserved,未使用。
  • section,每个符号都和本可重定向目标模块的一个节相关联,这个值是一个索引值,节头部表的索引值,节头部表包含了节的条目。

符号解析

链接器解析符号引用的方法是将每个引用与他输入的可重定位目标文件的符号表中一个确定的符号定义联系起来。

编译器值允许每个模块中每个本地符号只有一个定义。编译器还确保静态本地变量,它们也会有本地链接器符号,拥有唯一的名字。

对于全局符号,编译器会假设该符号是在其他某个模块中定义的,生成一个链接器符号表条目,并把它交给链接器处理。对全局符号的解析很棘手,还因为多个目标文件,可能会定义相同的符号。

链接器如何解析多重定义的全局符号

函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。

Unix链接器使用下面的规则来处理多重定义的符号:

  • 不允许有过个强符号。
  • 如果有一个强符号和多个弱符号,那么选择强符号。
  • 如果有多个弱符号,那么从这些弱符号中任意选择一个。

与静态库链接

将所有相关的目标模块打包成为一个单独的文件称为静态库,它可以用作链接器的输入。

如果不使用静态库,要用什么方法向用户提供c标准里的库函数?

  • 编译器辨认出对标准函数的调用。——c标准函数太多,编译器太复杂,同时一个标准函数变了,那么编译器就需要推出一个新的版本。

  • 所有的c标准函数都放入一个可重定位目标文件中。——那么每一个可执行目标文件,都将包含所有的c标准函数,体积过大。维护这个大的可重定位目标文件也麻烦,任意一个小的修改都要完整的编译所有c标准函数。

静态库的解决方法:相关的标准函数被编译成一个可重定位目标文件,然后将这些目标文件封装成一个静态库文件,然后应用程序通过在命令行上指定的单独文件名字来使用这些在库中定义的函数。

使用标准C库和数学库中函数的程序可以用如下形式编译:

gcc main.c /usr/lib/libm.a /usr/lib/libc.a

gcc -static,这个-static参数告诉编译器驱动程序,链接器应该构建一个完全链接的可执行目标文件,可以加载到存储器并运行,在加载时无需更进一步的链接。

在Unix系统中,静态库以一种称为存档的特殊文件格式存放在磁盘中。

重定位

一旦链接器完成了符号解析,就把代码中的每个符号引用和确定的一个符号定义联系起来。然后进行重定位。分为以下两步:

  • 重定位节和符号定义——所有相同类型的节合并为同一类型的新的聚合节。将运行时存储器地址赋给—聚合节—每个模块定义的每个节—每个符号。完成这一步之后,程序中的每个指令和全局变量都有唯一的运行时存储器地址了。

  • 重定位节中的符号引用——这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。——这一步依赖于重定位条目,这个条目就是.rel.text和.rel.data两个节。

重定位条目

汇编器生成目标模块,就是ELF格式的可重定位目标模块,模块中有符号,符号中有外部符号,汇编器不知道外部符号的位置,他会在符号表中标识其为UND类型,这就是ELF可重定位目标文件,他不知道,就标为UND,然后,他会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。代码的重定位条目在.rel.text,数据的重定位条目在.rel.data中。

ELF重定位条目的格式:

  • offset是需要被修改的引用的节偏移。
  • symbol标识被修改的引用应该指向的符号。
  • type告知链接器如何修改新的引用。

重定位符号引用

  • R _ 386 _ PC32:重定位一个使用32位PC相对地址的引用。当CPU执行一条使用PC相对寻址的指令时,它就将在指令中编码的32位值上加上PC的当前运行的值,得到有效地址,PC值通常是存储器中下一条指令的地址。

  • R _ 386 _ 32:重定位一个使用32位绝对地址的引用。通过绝对寻址,CPU直接使用在指令中编码的32位值作为有效地址,不需要进一步修改。

可执行目标文件

ELF可执行目标文件的格式:

ELF可执行文件被设计的很容易加载到存储器,可执行文件的连续的片被映射到连续的存储器段。段头部表描述了这种映射关系。

加载可执行目标文件

加载器是一段操作系统代码,这段代码驻留在存储器中。

任何Unix程序都可以通过调用execve函数来调用加载器,这个函数是Unix提供的系统接口之一。

加载器将可执行目标文件中的代码和数据从磁盘拷贝到存储器中,然后跳转到程序的第一条指令或入口点来执行该程序。——这一过程称为加载。

在32位系统中,代码段总是从地址0x0848000开始的。从栈的上部开始的段是为操作系统驻留存储器的部分的代码和数据保留的。

加载器跳转到程序的入口点,这个入口点就是一个符号的地址,,_start这个符号源自ctrl.o。ctrl.o可以预见是标准函数的可重定位目标文件或者系统函数的可重定位目标文件。

_start是个函数,在这个函数中,会依次的调用一些其他的初始化例程。其中atexit例程附加了一系列的应用程序正常中止时应该调用的程序。exit函数运行atexit注册的函数,然后通过调用_exit将控制返回给操作系统。接着,调用main程序。

动态链接共享库

共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并和一个在存储器中的程序连接起来。这个过程称为动态链接,由动态链接器执行。

共享库在Unix中用后缀.so表示。在微软操作系统中称为DLL。

共享库的共享通过两种不同的方式执行:

  • 一个库只有一个.so文件,这是对于磁盘来说的,比如libc.so,一个ubuntu只有一个libc.so,或者说某一个版本的,只有一个libc.so。

  • 对于存储器,.text节的一个副本可以被不同的正在运行的进程共享。

当加载器加载和运行一个编译包含共享库的可执行文件时,加载器会注意到一个.interp节,然后,加载器会加载和运行一个动态链接器。(动态链接器本身就是一个共享目标)。

动态链接器执行重定位完成链接任务。最后,动态链接器将控制传递给应用程序。

从应用程序中加载和链接共享库

动态链接在现实世界中的例子:分发软件,构建高性能web服务器。

linux系统为动态链接器提供了一个简单的接口,允许应用程序在运行时加载和链接共享库。

#include <dlfcn.h>
void *dlopen(const char * filename, int floag);
#成功,返回指向句柄的指针,若出错则为NULLvoid * dlsym(void *handle, char *symbol);
#第一个参数是上面函数返回的句柄的指针,第二个参数是符号的名字
#成功,返回指向句柄的指针,若出错则为NULLint dlclose(void *handle);
#如果没有其他共享库正在使用这个共享库,那么就卸载该共享库。
#成功返回0,出错返回-1const char* dlerror(void);
#上面的3个函数运行之后,运行这个函数,可以看看最近发生的最近的错误,如果没有错误,就返回NULL

与位置无关的代码(PIC)

多个进程是如何共享程序的一个拷贝:

使链接器不需要修改库代码就可以在任何地址加载和执行这些代码,这要求编译库代码的时候要实现这种功能。

这样的代码叫做PIC(位置无关的代码),GCC用-fPIC选项来实现。

PIC数据引用

无论我们在存储器中的何处加载一个目标模块(包括共享目标模块),数据段总是被分配成紧随在代码段后面。因此,代码段中任何指令和数据段中任何变量之间的距离都是一个运行时常量,与代码和数据段的绝对位置无关。

为了运用这个事实,编译器在共享模块的数据段开始的地方创建了一个表,叫做GOT。

PIC函数调用

PIC使用同样的方法来解析外部过程调用,ELF编译系统使用延迟绑定的技术。通过以下两个数据结构:

  • GOT
  • 过程连接表

处理目标文件的工具

GNU binutils包:

  • AR:创建静态库,插入、删除、列出和提取成员。
  • STRINGS:列出一个目标文件中所有可打印的字符串。
  • STRIP:从目标文件中删除符号表信息。
  • NM:列出一个目标文件的符号表中定义的符号。
  • SIZE:列出目标文件中节的名字和大小。
  • READELF:显示一个目标文件的完整结构,包括ELF头中编码的所有信息。包括SIZE和NM的功能。
  • OBJDUMP:所有二进制工具之母。能够显示一个目标文件中所有的信息。它最大的作用是反汇编.text中的二进制指令。
  • LDD:列出一个可执行文件在运行时所需要的共享库。

转载于:https://www.cnblogs.com/July0207/p/5376156.html

《深入理解计算机系统》课本第七章自学笔记——20135203齐岳相关推荐

  1. 《深入理解计算机系统》第七章读书笔记

    <深入理解计算机系统>第七章读书笔记 第七章:连接 连接 1.连接:将各种代码和数据部分收集起来并组合成为一个单一文件的过程.这个文件可被加载或拷贝到存储器并执行. 2.连接可以执行于编译 ...

  2. 《深入理解计算机系统》第七章 链接

    <深入理解计算机系统>第七章 链接 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(货被拷贝)到存储器并执行. 链接的时机 编译时,也就是在源代码被翻译成 ...

  3. 《深入理解计算机系统》第七章——链接知识点总结

    目录 7.1编译器驱动程序 7.2静态链接 7.3目标文件 7.4可重定位目标文件 7.5符号和符号表 7.6符号解析 • 静态库(.a archive files) 7.1编译器驱动程序 7.2静态 ...

  4. 计算机系统结构sw指令集,《深入理解计算机系统》第三章学习笔记

    ###通过本周的学习,总结出一下知识内容 ###机器级代码 计算机系统使用了多种不同形式的抽象,利用更简单的抽象模型来隐藏实现的细节. 对于机器级编程来说,其中两种抽象尤为重要: 1.指令集体系结构( ...

  5. 深入理解计算机系统-第七章(链接)笔记

    深入理解计算机系统-第七章(链接)笔记 背景 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程 这个文件可被加载(拷贝)到存储器中并执行: 链接可以执行于编译时,也就是源代码翻译成机器码 ...

  6. 《深入理解计算机系统》(CSAPP)读书笔记 —— 第三章 程序的机器级表示

    本章主要介绍了计算机中的机器代码--汇编语言.当我们使用高级语言(C.Java等)编程时,代码会屏蔽机器级的细节,我们无法了解到机器级的代码实现.既然有了高级语言,我们为什么还需要学习汇编语言呢?学习 ...

  7. 《深入理解计算机系统》第2章 信息的表示与处理

    <深入理解计算机系统>第2章 信息的表示与处理 允许任何人转载,仅作为学习交流. 萌新一枚,本着交流学习经验的心态,写了这篇文章.若文章有误,还请各位大佬指正,谢谢(๑•̀ㅂ•́) ✧ 2 ...

  8. 描述性物理海洋学--第七章学习笔记

    第七章学习笔记--海洋环流的动力过程 不稳定理论 判别方法: 大尺度不稳定 中尺度不稳定 控制方程 地流动力学参数 近似简化 正斜压海洋 基本洋流 地转流 风生Ekman 环流 上升流 朗缪尔环流 惯 ...

  9. 第七节 可执行程序的装载——20135203齐岳

    第七节 可执行程序的装载 By 20135203齐岳 本周的主要内容: 可执行程序是如何得到的以及可执行程序的目标文件格式 动态库 &动态链接库 系统调用sys_exec函数的执行过程 预处理 ...

最新文章

  1. MVP Summit 2008 照片纪实(二)- 旧金山,Google总部和Stanford大学
  2. OpenGL教程——GLUT初始化
  3. Android开发之蓝牙--扫描已经配对的蓝牙设备
  4. 关于SAP Commerce Cloud OCC API url里不包含user信息的问题
  5. 如何查看mysql my.ini_MySQL学习笔记(一)
  6. [LeetCode] 141. Linked List Cycle 单链表判圆算法
  7. 一个基于WF的业务流程平台
  8. javascript class static
  9. 十大必须掌握的 Chrome 浏览器开发者工具
  10. Effective C# Item18:实现标准Dispose模式
  11. TF-Lite极简参考-环境搭建
  12. 2021肿瘤早筛行业研究报告
  13. Git的安装步骤、配置(解决Git官网下载速度慢、无法下载,需要授权)
  14. 数据透视表:多重合并计算数据区域
  15. redis 经纬度_【SpringBoot DB 系列】Redis 高级特性之 GEO
  16. 最新悬赏猫任务接单发布系统APP三端源码 附带视频详细搭建教程
  17. 亚马逊云(AWS)、微软云(Azure)、阿里云性能对比之哪家好?
  18. php extension kdb+,kdb+ 中机器学习
  19. 饼状图的实现方法html,D3.js实现饼状图的方法详解
  20. html 应用程序主机 自动关闭,服务器会话连接自动关闭怎么办?Web服务器 -电脑资料...

热门文章

  1. access vba代码大全_这本VBA经典图书终于做活动了,还是5折!
  2. DSSD(Deconvolutional Single Shot Detector)
  3. 人脸识别无人机燃爆《战狼2》 它真的存在吗?
  4. LCCUP 力扣杯2020秋季编程大赛题解
  5. DC基础学习(四)综合优化的三个阶段
  6. 宇视网络视频录像机如何修改宇视摄像机网络地址
  7. 甜叶菊提取物甜菊糖苷分离纯化吸附树脂
  8. 【报告分享】2020年中国宠物消费市场分析报告-IT桔子(附下载)
  9. 移动端高清、多屏幕适配方案
  10. 前端开发中自己常见问题