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

一、概述

C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可加载、可执行代码的过程。

过程图解如下: 

  1. 预处理器:将.c 文件转化成 .i文件,使用的gcc命令是:gcc –E,对应于预处理命令cpp;
  2. 编译器:将.c/.h文件转换成.s文件,使用的gcc命令是:gcc –S,对应于编译命令 cc –S;
  3. 汇编器:将.s 文件转化成 .o文件,使用的gcc 命令是:gcc –c,对应于汇编命令是 as;
  4. 链接器:将.o文件转化成可执行程序,使用的gcc 命令是: gcc,对应于链接命令是 ld;
  5. 加载器:将可执行程序加载到内存并进行执行,loader和ld-linux.so。

 

二、编译过程

编译过程又可以分成两个阶段:编译和汇编。

2.1编译

编译是指编译器读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码

源文件的编译过程包含两个主要阶段:

第一个阶段是预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。

主要是以下几方面的处理:

  1. 宏定义指令,如 #define a b 对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。
  2. 条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉
  3. 头文件包含指令,如#include "FileName"或者#include 等。 该指令将头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。
  4. 特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用,这涉及到头文件的定位即搜索路径问题。头文件搜索规则如下:

  1. 所有header file的搜寻会从-I开始
  2. 然后找环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH指定的路径
  3. 再找默认目录(/usr/include、/usr/local/include、/usr/lib/gcc-lib/i386-linux/2.95.2/include......)

 

第二个阶段编译、优化阶段编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。 

 

2.2汇编

汇编实际上指汇编器(as)把汇编语言代码翻译成目标机器指令的过程。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:

  • 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
  • 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

 

2.3目标文件(Executable and Linkable Format)

  1. 可重定位(Relocatable)文件:由编译器和汇编器生成,可以与其他可重定位目标文件合并创建一个可执行或共享的目标文件;
  2. 共享(Shared)目标文件:一类特殊的可重定位目标文件,可以在链接(静态共享库)时加入目标文件或加载时或运行时(动态共享库)被动态的加载到内存并执行;
  3. 可执行(Executable)文件:由链接器生成,可以直接通过加载器加载到内存中充当进程执行的文件。

 

2.4 静态库与动态库

静态库(static library)就是将相关的目标模块打包形成的单独的文件。使用ar命令。

静态库的优点在于:

  • 程序员不需要显式的指定所有需要链接的目标模块,因为指定是一个耗时且容易出错的过程;
  • 链接时,连接程序只从静态库中拷贝被程序引用的目标模块,这样就减小了可执行文件在磁盘和内存中的大小。

动态库(dynamic library)是一种特殊的目标模块,它可以在运行时被加载到任意的内存地址,或者是与任意的程序进行链接。

动态库的优点在于:

  • 更新动态库,无需重新链接;对于大系统,重新链接是一个非常耗时的过程;
  • 运行中可供多个程序使用,内存中只需要有一份,节省内存。

 

三、链接过程

链接器主要是将有关的目标文件彼此相连接生成可加载、可执行的目标文件。链接器的核心工作就是符号表解析和重定位。

3.1 链接的时机:

  1. 编译时,就是源代码被编译成机器代码时(静态链接器负责);
  2. 加载时,也就是程序被加载到内存时(加载器负责);
  3. 运行时,由应用程序来实施(动态链接器负责)。

3.2 链接的作用(软件复用):

  1. 使得分离编译成为可能;
  2. 动态绑定(binding):使定义、实现、使用分离

3.3 静态库搜索路径(由静态链接器负责)

  1. gcc先从-L寻找;
  2. 再找环境变量LIBRARY_PATH指定的搜索路径;
  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的。

3.4 动态库搜索路径(由动态链接器负责)

  1. 编译目标代码时指定的动态库搜索路径-L;
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
  4. 默认的动态库搜索路径/lib /usr/lib/ /usr/local/lib

3.5 静态链接(编译时)

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

为创建可执行文件,链接器必须要完成的主要任务:

  1. 符号解析:把目标文件中符号的定义和引用联系起来;
  2. 重定位:把符号定义和内存地址对应起来,然后修改所有对符号的引用。

关于符号表和符号解析以及重定位的分析后续学习。

3.6 动态链接(加载、运行时)

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


四、加载过程

加载器把可执行文件从外存加载到内存并进行执行。 Linux中进程运行时的内存映像如下:

 

 加载过程如下:

加载器首先创建如上图所示的内存映像,然后根据段头部表,把目标文件拷贝到内存的数据和代码段中。然后,加载器跳转到程序入口点(即符号_start 的地址),执行启动代码(startup code),启动代码的调用顺序如所示:

 

五、处理目标的常用工具

UNIX系统提供了一系列工具帮助理解和处理目标文件。GNUbinutils 包也提供了很多帮助。这些工具包括:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab bwdist

bwdist函数用于计算元素之间的距离。 举个例子: 如果a 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0 0 1 1 1 0 0 0 0 0 0 那么: [D,L]bwdist(a); D 1.4142 1.0000 1.0000 1.0000 1.4142 1.0000 0 0 0 1.0000 1.0000 0 0 0 1.0000 1.0000 0 0 0 1.0000 1.4142 1.000…

js函数库-D3

推荐: https://www.cnblogs.com/createGod/p/6884629.html转载于:https://www.cnblogs.com/john-hwd/p/10616166.html

配置Ubuntu Server高速apt-get源

今天刚装上Ubuntu Server 12,默认的apt-get源比较慢。更改一下源地址。 方法: 1、修改源地址: cp /etc/apt/sources.list /etc/apt/sources.list.bak vim /etc/apt/sources.list 加入如下内容(中科大的): …

matlab find

find函数用于返回所需要元素的所在位置 (位置的判定:在矩阵中,第一列开始,自上而下,依次为1,2,3...,然后再从第二列,第三列依次往后数)find(A)返回矩阵A中非零元素所在位…

红黑树操作详解——很形象的过程

红黑树是一种很好的自平衡二叉排序树,在此,给出一个网友给出的红黑树操作详解: https://segmentfault.com/a/1190000012728513 里面给出了红黑树的详细操作,过程很形象!!! 结合可视化数据结构网…

地图事件触发_使用地图触发功能处理相干事件

地图事件触发本文介绍如何通过使用映射触发器来处理一致性事件。 基本上,建议使用Oracle Coherence中的分布式数据管理来研究Oracle Coherence API的基本配置和实现。 映射触发器是Oracle Coherence提供最高度定制的缓存管理系统的最重要功能之一。 MapTrigger表示…

C++ 内存对齐

注:本文代码测试环境为win7 X64 cpu, 编译器为gcc4.7.1 和 vs2010 内存对齐是编译器为了便于CPU快速访问而采用的一项技术 我们先从一个例子开始,对下面的类(或者结构体) class node { char c; int i; short s; }no; sizeof(no)的值是多少呢,…

matlab sub2ind与ind2sub

sub2ind与ind2sub函数 A [1 2 3; 4 5 6;7,8,9]; >> fsub2ind(size(A), 2, 3) f 8 即把矩阵A中第二行第三列的元素的全下标标识(2,3)转换为对应的单下标标识8,即该元素从第一列顺次数过去是第八号元素。 而ind2sub则用于把矩阵中…

Spring Boot和Angular 2入门食谱

我主要是一名服务开发人员,必须不时创建一些可传递的UI。 我精通基于AngularJS1的基本UI,并且可以使用之前概述的方法来完成工作。 遗憾的是,随着Angular 2的出现,我不得不将以前的方法抛诸脑后,而现在使用Spring Boot…

Robbers' watch CodeForces - 685A (暴力)

大意: 一天n小时, m分钟, 表以7进制显示, 求表显示数字不同的方案数 注意到小时和分钟部分总长不超过7, 可以直接暴力枚举. 关键要特判0, 0的位数要当做1来处理 #include <iostream> #include <algorithm> #include <cstdio> #include <math.h> #inclu…

什么是javax.ws.rs.core.context? [第5部分]

如何使用Context批注 在什么是javax.ws.rs.core.context的第4部分中&#xff1f; 您学习了如何使用Context批注将HttpServletResponse和HttpServletRequest类注入资源方法。 在本文中&#xff0c;您将学习如何使用其余两个仅在servlet容器中可用的类&#xff0c;它们是&#x…

Linux字符界面和图形界面

Ubuntu图形界面和字符界面的切换 Ubuntu和其他的Linux系统一样&#xff0c;有图形界面和字符界面&#xff0c;同时能够设置默认的启动界面。 linux的显示界面分为命令行的字符界面和图形界面&#xff0c;我们可以设置linux的默认启动的显示界面。然后也可以手动的来回的切换。 …

matlab conv2

conv2函数----------------------------------------1、用法 1. Cconv2(A,B,shape); %卷积滤波 A:输入图像&#xff0c;B:卷积核 假设输入图像A大小为ma x na&#xff0c;卷积核B大小为mb x nb&#xff0c;则 当shapefull时&#xff0c;返回全部二维卷积结…

面试趣味题

题目&#xff1a;一元钱可以买到一瓶水,两个空瓶子可以换一瓶水,小明有20元钱,问最多可以喝多少瓶水? 在面试的时候做了一个这种逻辑题。当时只是要求写出最后的结果。没有要求用代码来实现。我下面准备先分析一下&#xff0c;然后再转换成代码去实现。 根据这个示意图可以简单…

mongodb 持久性_多语言持久性:带有MongoDB和Derby的EclipseLink

mongodb 持久性从现在开始&#xff0c;多语种持久性一直是新闻。 从2011年底开始&#xff0c;在著名的Fowler职位的激励下&#xff0c;我看到了更多更好的主意。 最新的一个是公司内部的学生项目&#xff0c;我们在其中使用Scala作为后端数据&#xff0c;将数据持久存储到Mongo…

linux 动态库文件stripped属性理解

在centos 6.2下用file命令查看文件信息的时候&#xff0c;显示如下&#xff1a; libcom_err.so.2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped libcrypto.so.10: ELF 64-bit LSB shared object, x86-64, version 1 (SYS…

matlab gradient

gradient函数 >> x[6,9,3,4,0;5,4,1,2,5;6,7,7,8,0;7,8,9,10,0]x 6 9 3 4 0 5 4 1 2 5 6 7 7 8 0 7 8 9 10 0 >> [Fx,Fy]gradient(x) Fx 3.0000 -1.5000 -2.5000 -1.5000…

第六章 传输层

思维导图&#xff1a; 一、 概述 与数据链路层关系 提供可靠的、高效的、性价比高的数据传输输出实体 传输层利用网络层的服务&#xff0c;为它的上层应用层提供服务 网络层与传输层不同点&#xff1a; 网络层运行在由承运商操作的路由器上&#xff0c;因此用户无法真正控制到网…

Kanvas:从您的ANTLR语法生成一个简单的IDE

什么是编辑器&#xff1f; 对我来说&#xff0c;编辑器是我工作中使用的主要工具。 作为语言工程师&#xff0c;我创建新的语言&#xff0c;使用现有的语言&#xff0c;并且需要其他工具来使用它们。 我希望能够在一个定制的IDE中将所有这些黑客一起入侵&#xff0c;我可以为我…

函数名/函数地址/函数指针

转自&#xff1a;http://hi.baidu.com/%C6%BF%D6%D0%B5%C4%C5%AE%CE%D7/blog/item/387db9ddaa54d0a9cd1166fa.html 函数指针&#xff1a;1。指针变量 2。指针变量指向函数 这正如用指针变量可指向整型变量、字符型、数组一样。 在编译时&#xff0c;每一个函数都有一个入口地址…