第七章——链接

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

  2、链接可以执行于编译时,加载时,运行时。

7.1编译器驱动程序

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

7.2 静态链接

  1、像Unix ld程序这样的静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载运行的可执行目标文件作为输出。

  2、输入的可重定位目标文件由各种不同的代码和数据节组成。

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

  (1)符号解析。目标文件定义和引用符号。符号解析的目的是将每个符号引用刚好和一个符号定义联系起来。

  (2)重定位。编译器和汇编器生成从地址0开始的代码和数据节。

7.3 目标文件

  1、目标文件有三种形式:

    (1)可重定位目标文件;

    (2)可执行目标文件;

    (3)共享目标文件;

  2、编译器和汇编器生成可重定位目标文件(包括共享目标文件)。链接器生成可执行目标文件

  3、各个系统之间,目标文件格式都不相同。直到今天,可执行文件仍然被称为a.out文件。

7.4 可重定位目标文件

  1、典型的ELF可重定位目标文件的格式

   

7.5符号和符号表

  1、每个可重定位目标模块m都有一个符号表,它包含m所定义和引用的符号的信息。

  2、在链接器的上下文中,有三种不同的符号:

    (1)由m定义并能被其他模块引用的全局符号;

    (2)由其他模块定义并被模块m引用的全局符号(外部符号);

    (3)只被模块m定义和引用的本地符号;

7.6符号解析

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

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

  1、在编译时,编译器向汇编器输出每个全局符号,或者是强或者是弱,而汇编器把这个信息隐含的编码在可重定位目标文件的符号表里。

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

  3、根据强弱符号的定义,Unix链接器使用下面的规则来处理多重定义的符号:

    规则1:不允许有多个强符号。

    规则2:如果有一个强符号和多个弱符号,那么选择强符号。

    规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个。

7.6.2 与静态库链接

  1、所有的编译系统都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库。当链接器构造一个输出的可执行文件时,它只拷贝静态库里被应用程序引用的目标模块。

  2、在链接时,链接器将只拷贝程序引用的目标模块。

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

  4、存档文件是一组连接起来的可重定位目标文件的集合。存档文件名由后缀.a标识。

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

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

  1、在符号解析的阶段,链接器从左到右按照他们在编译器驱动程序命令行上出现的相同的顺序来扫描可重定位目标文件和存档文件。(驱动程序自动将命令行中所有的.c文件翻译为.o文件)

  2、在这次扫描中,链接器维持一个可重定位目标文件的集合E(这个集合中的文件会被合并起来形成可执行文件),一个未解析的符号(即引用了但是尚未定义的符号)集合U,以及一个在前面输入文件中已定义的符号集合D。初始时,E、U和D都是空的。

7.7   重定位

  1、重定位由两步组成:

    (1)重定位节和符号定义

    (2)重定位节中的符号引用

  2、无论何时汇编器遇到对最终位置未知的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。

  3、代码的重定位条目放在.rel.text中,已初始化数据的重定位条目放在.rel.data中。

  4、PC值通常是存储器中下一条指令的地址。

7.8   可执行目标文件

  1、C程序开始时是一组ASCII文本文件,已经被转化为一个二进制文件,且这个二进制文件包含加载程序到存储器并运行它所需的所有信息。

  2、典型的ELF可执行文件中的各类信息:

  

  3、可执行目标文件的格式类似于可重定位目标文件的格式。ELF头部描述文件的总体格式。它还包括程序的入口点,也就是当程序运行时要执行的第一条指令的地址。

7.9   加载可执行目标文件

  1、加载器将可执行目标文件中的代码和数据从磁盘拷贝到存储器中,然后通过跳转到程序的第一条指令入口点来运行该程序。这个将程序拷贝到存储器并运行的过程叫做加载。

  2、在32位Linux系统中,代码段总是从地址0x08048000处开始。

  3、数据段是在接下来的下一个4KB对齐的地址处。运行时堆在读/写段之后接下来的第一个4KB对齐的地址处,并通过调用malloc库往上增长。

  4、还有一个段是为共享库保留的。

  5、用户栈总是最大的合法用户地址开始,向下增长的(向低存储器地址方向增长)。从栈的上部开始的段是为操作系统驻留存储器的部分(内核)的代码和数据保留的。

7.10 动态链接共享库

  1、静态库和所有的软件一样,需要定期维护和更新。

  2、共享库是致力于解决静态库缺陷的一个现代创新产物。

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

  4、共享库也称为共享目标,在Unix系统中通常用.so后缀来表示,微软的操作系统大量的利用了共享库,它们称为DLL(动态链接库)。

  5、共享库是以两种不同的方式来“共享”的。首先,在任何给定的文件系统中,对于一个库只有一个.so文件。所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被拷贝和嵌入到引用它们的可执行的文件中。其次,在存储器中,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

  (1)一种共享方式就是隐式链接,基本的思路:当创建可执行文件时,静态执行一些链接,然后在程序加载时,动态完成链接过程。

  (2)一种共享方式就是“显式链接”,应用程序被加载时,动态链接器加载和链接共享库的情景。应用程序还可能在它运行时要求动态链接器加载和链接任意共享库,而无需在编译时链接那些库到应用中。

  

  5、动态链接器通过执行下面的重定位完成链接任务:

    (1)  重定位libc.so的文本和数据到某个存储器段。

    (2)  重定位libvector.so的文本和数据到另一个存储器段。

    (3)  重定位p2中所有对由libc.so和libvector.so定义的符号的引用。

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

  1、dlopen函数加载和链接共享库filename。

  2、dlsym函数的输入是一个指向前面已经打开共享库的句柄和一个符号名字,如果该符号存在,就返回符号的地址,否则返回NULL。

  3、如果没有其他共享库正在使用这个共享库,dlclose函数就卸载该共享库。

  4、dlerror函数返回一个字符串,它描述的是调用dlopen、dlsym或者dlclose函数时发生的最近的错误,如果没有错误发生,就返回NULL。

7.12 与位置无关的代码(PIC)

  1、共享库的一个主要目的就是允许多个正在运行的进程共享存储器中相同的库代码,因而节约宝贵的存储器资源。

  2、不需要链接器修改库代码就可以在任何地址加载和执行这些代码。这样的代码叫做与位置无关的代码(PIC)。

  3、ELF编译系统使用一种有趣的技术,叫做延迟绑定,将过程地址的绑定推迟到第一次调用该过程时。

  4、延迟绑定是通过两个数据结构之间简洁但又有些复杂的交互来实现的,这两个数据结构是:GOT和过程链接表(PLT)。GOT是.data节的一部分,PLT是.text节的一部分。

7.13 处理目标文件的工具

  1、在Unix系统中有大量可用的工具可以帮助理解和处理目标文件。

  AR:创建静态库,插入、删除、列出和提取成员。

  STRINGS:列出一个目标文件中所有可打印的字符串。

  STRIP:从目标文件中删除符号的信息。

  NM:列出一个目标文件的符号表中定义的符号。

  SIZE:目标文件中节的名字和大小。

  READELF:显示一个目标文件的完整结构,包括ELF头中的编码的所有信息。包含SIZE和NM的功能。

  OBJDUMP:所有二进制工具之母,能够显示一个目标文件中所有的信息。它最大的作用是反汇编.text节中的二进制指令。

  LDD:列出一个可执行文件在运行时所需要的共享库。

7.14 小结

  1、链接可以在编译时由静态编译器来完成,也可以在加载时和运行时由动态链接器来完成。

  2、链接器处理称为目标文件的二进制文件,它有三种不同的形式:可重定位的、可执行的和共享的。

  3、链接器的两个主要任务是符号解析和重定位。

  4、静态链接器是由像GCC这样的编译驱动器调用的。

  5、多个目标文件可以被连接到一个单独的静态库中。

  6、加载器将可执行文件的内容映射到存储器,并运行这个程序。

  7、被编译为位置无关代码的共享库可以加载到任何地方,也可以在运行时被多个进程共享。

Linux内核分析——第七章 链接的更多相关文章

  1. LINUX内核分析第七周学习总结:可执行程序的装载

    LINUX内核分析第七周学习总结:可执行程序的装载 韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  2. Linux内核分析 第七周 可执行程序的装载

    张嘉琪 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核分析 第七 ...

  3. LINUX内核分析第七周学习总结

    LINUX内核分析第七周学习总结 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.c ...

  4. Linux内核分析第七周———可执行程序的装载

    Linux内核分析第七周---可执行程序的装载 李雪琦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/US ...

  5. Linux内核分析第七周学习笔记——Linux内核如何装载和启动一个可执行程序

    Linux内核分析第七周学习笔记--Linux内核如何装载和启动一个可执行程序 zl + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study. ...

  6. Linux内核分析(七)----并发与竞态

    原文:Linux内核分析(七)----并发与竞态 Linux内核分析(七) 这两天家里的事好多,我们今天继续接着上一次的内容学习,上次我们完善了字符设备控制方法,并深入分析了系统调用的实质,今天我们主 ...

  7. Linux内核分析第四章 读书笔记

    Linux内核分析第四章 读书笔记 第一部分--进程调度 进程调度:操作系统规定下的进程选取模式 面临问题:多任务选择问题 多任务操作系统就是能同时并发地交互执行多个进程的操作系统,在单处理器机器上这 ...

  8. linux内核分析第3章&第18章读书笔记

    linux内核分析第3章&第18章读书笔记 第三章 进程管理 进程:处于执行期的程序(目标码存放在某种存储介质上) 包含资源:可执行程序代码,打开的文件,挂起的信号,内核内部数据,处理器状态, ...

  9. 20135327郭皓--Linux内核分析第七周 可执行程序的装载

    第七周 可执行程序的装载 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 ...

随机推荐

  1. java news website

    http://javacrawl.com/ https://dzone.com/java-jdk-development-tutorials-tools-news https://www.infowo ...

  2. MVC设计模式在网站中的应用

    MVC设计模式在网站中的应用 以淘宝的购物车为例 一.结合六个基本质量属性 可修改性 采用MVC设计模式的时候,可以将视图.模型.控制器分析,将用户动作.数据表示.应用数据分离开来,如果用户需要以不同 ...

  3. SAP S4/HANA BP屏幕增强添加自定义字段(BDT方式)

    喜欢博主的读者也许会意识到,这是本博客中第一篇有关屏幕增强的文章.之前没有总结过相关的东西,除了因为相关经验有限之外,我个人也是不喜欢所谓dynpro编程的,它有许多“潜规则”一样的东西要记住,想要运 ...

  4. var a = {m:1}; var b = a; a.n = b ={n:1}; console.log(a);console.log(b);

    var a = {m:1}; var b = a; a.n = b ={n:1}; console.log(a); console.log(b); 确定b为{n:1},所以a为 {m:1,n:{n:1 ...

  5. cglib之Enhancer

    1. 背景 cglib库的Enhancer在Spring AOP中作为一种生成代理的方式被广泛使用.本文针对Enhancer的用法以实际代码为例作一些介绍. 2. Enhancer是啥 Enhance ...

  6. 实时监听input输入的变化(兼容主流浏览器)【转】

    遇到如此需求,首先想到的是change事件,但用过change的都知道只有在input失去焦点时才会触发,并不能满足实时监测的需求,比如监测用户输入字符数. 在经过查阅一番资料后,欣慰的发现firef ...

  7. gitlab备份及恢复

    gitlab备份步骤 https://blog.csdn.net/qq_31666147/article/details/79844107 gitlab版本  下载地址 https://mirrors ...

  8. MySQL 误操作后如何快速恢复数据~!~!~

    基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,MySQL误操作后如何快速回滚?比如,delete一张表,忘加限制条件,整张表没了.假如这还是线上环境核心业务数据,那这事就闹大了 ...

  9. C++ 文件和流

    到目前为止,我们已经使用了 iostream 标准库,它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流. 本教程介绍如何从文件读取流和向文件写入流.这就需要用到 C++ ...

  10. Redis 参数说明

    4. Redis的配置 4.1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize no 4.2. 当Redis以守护进程方式运行时,Redis ...