发表时间:2022-03-26来源:网络
本文旨在以hello world程序在ubuntu 16.04 x86_64机器上运行为例,详细讲述这个程序从编译、链接(包括动态链接和静态链接)到加载到运行这个过程中,工具链gcc,运行库glibc,内核,他们是怎么分工协作让这个程序顺利完成加载和运行的;

总体流程如下图:

以hello world程序为例,使用gcc --verbose参数查看编译的详细过程如下:

其中cc1为gcc工具链的C编译器,预处理和编译都由该工具完成,as为汇编器,完成汇编工作,collect2为链接器ld的封装形式,最终负责链接收尾工作;

汇编阶段就是使用as汇编器简单的参照指令编码表将汇编代码翻译为机器指令,并将解析汇编文件中重要的部分形成辅助的信息结构并(符号,函数名等)以分节(section)的方式组织起来, 汇编之后的hello文件,使用命令readelf -S main.o 可以查看该文件的分节结构:

可以看到该汇编文件由12个段组成,其中.symtab为符号表,记录了该文件中所有全局符号以及函数名引用但是未定义的符号名等,.rela.text为代码重定位,这些段都将在链接阶段被使用用来构成最终的可执行文件;
链接是编译的最后一个阶段,链接主要是将多个文件(目标文件或者库文件)合并为可执行文件,并分配运行时地址,然后进行重定位工作,在进行链接的时候链接器使用静态库来解析引用的,在符号解析阶段,链接器从左到右按照他们在编译器驱动命令行上出现的相同顺序来扫描可重定位目标文件和库文件,链接器维持一个可重定位目标文件的集合E,一个未解析的符号集合U吗,一个全局已定义符号表D,在最初RUD都为空;
对于命令行上的每个输入文件F,链接器都会判断F是一个目标文件还是一个库文件,如果F是一个目标文件,那么链接器把F添加到E中,再将F中的所有符号更新到UD,并继续解析下一个文件;
如果输入文件为库文件,那么链接器就尝试匹配U中的未解析的符号,库文件由多个目标文件所组成,如果某个库文件的目标文件成员M,定义了一个符号来解析U中的一个引用,那么就将M加到E中,并且链接器将M成员的符号更新到U,D中,接着处理库文件中的下一个目标文件,如果该目标文件中的所有符号均未被引用,则丢弃该目标文件(不会被加到E中),接着链接器将处理下一个库文件(或者目标文件);
直至链接器处理完所有的输入文件,U是非空的,链接器将报链接未定义的错误,否则,他会合并和重定位E中的目标文件,构建出可执行文件;
注意:由此可以看出 链接的时候库文件和目标文件的顺序非常重要,依赖项一般要放在命令行的最后面,当库文件互相依赖的时候则需要重复书写库文件,例如:gcc main.o add.a sub.a add.a
链接又分为动态链接和静态链接,基于动态链接的优势,一般linux 机器中默认都使用动态链接的方式进行链接工作,我们将分别进行叙述;
静态链接:使用命令gcc -static main.o -o main -v 查看helloworld程序的详细链接过程如下:

其中collect2 是链接器ld的封装形式,可以看到链接器最终会将这些目标文件crt1.o,crti.o,crtbegin.o,main.o,crtn.o,crtend.o 和库-lgcc,-lc,-lgcc_s(除非我们制定链接静态库,默认情况下-lxx执行的链接库都是动态库)中所必需的的成员目标文件链接成一个可执行文件main;
正如上面所说的,链接器解析库文件之后将会获得该程序所必需的库文件的成员目标文件(.o文件)以及其他目标文件,我们知道目标文件是以各个节(section)组织起来的一个结构,将目标文件合并的过程就是将节进行合并,静态链接的过程就是把所有目标文件的相似节进行合并;

(该图线条只是画出了简要的步骤)到此我们可以总结静态链接的过程为:
首先链接器将以本节最开始的算法扫描gcc命令行中所有.o文件以及库文件中必要的.o文件,将这些.o文件的相同节进行合并,然后进行重定位工作最终形成可执行文件,注意在最终形成可执行文件可以使用命令readelf -S mains查看共有32个段,相比原来目标文件main.o的12个要大得多,不仅仅是因为多个.o文件的合并,这个过程中可执行文件中还要生成一些段表结构,例如该可执行程序被装载时如何映射到内存的策略这些信息都以额外辅助节的形式存储在可执行文件中,这将指导并辅助该可执行程序正确的加载到内存并完成运行;
动态链接:使用命令gcc -static main.o -o main -v 查看helloworld程序的详细链接过程如下:

可以对比静态链接,动态链接和静态链接所链接的文件都基本一致,唯一不同的是动态链接必须要指定动态链接器的路径,如图中ld-linux-x86-64.so.2为该程序的动态连接器;动态链接的编译时链接(链接还分为编译时链接和运行时链接,静态链接只有编译时链接,而动态链接有编译时链接和运行时链接,与GCC相关的为编译时链接)过程比较简单;
动态链接的流程与静态链接较为相似,不同的地方在于:
上一篇:用命令行编写第一个程序(二)
下一篇:用文本文档写java代码
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14