主页 程序员的自我修养 ——链接、装载与库读书笔记(二)
Post
Cancel

程序员的自我修养 ——链接、装载与库读书笔记(二)

此系列为我学习经典的读书笔记,目的是理清知识脉络帮助理解记忆,内容深度、质量远远不及原书。如果您对相关知识感兴趣,强烈建议阅读原书程序员的自我修养

带着问题学习

  • 源代码到可执行文件都经历了哪些步骤
  • 编译器做了什么工作
  • 什么是静态链接,静态链接都包含哪些步骤

可执行文件的生成

源代码到可执行文件的生成可分为预处理(Prepressing)编译(Compilation)汇编(Assembly)链接(Linking),四个步骤。

预处理

以 C 语言为例,预处理主要是处理源代码中以“#”开头的那些预处理指令,规则如下:

  • 将所有 “#define” 删除并展开宏定义
  • 处理所有条件预编译指令比如 “#if”、“#ifdef”、“#elif”、“#else”、“#endif”
  • 处理 “#include” 预编译指令,将被包含的文件插入到预编译位置。递归进行,被包含文件可能还包含其他文件
  • 删除所有注释
  • 添加行号与文件名标识,以便编译器产生调试信息
  • 保留 #pragma 编译器指令,因为编译器必须使用它们

编译

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后产生相应的汇编文件。

汇编

汇编是汇编器将汇编代码转变成机器可执行的指令的过程。汇编指令与机器指令几乎一一对应,所以汇编器直接翻译就可以了。

链接

将中间文件进行链接最终生成可执行文件。(此篇文章主要记录编译器工作,链接相关以后会分析到)

编译器做了什么

简单来说,编译器就是将高级语言翻译成机器语言的一个工具。

我们以一行 C 语言为例:

array[index] = (index + 4) * (2 + 6) CompilerExpression.c

词法分析

首先源代码进入扫描器(Scanner),扫描器利用有限状态机(Finite State Machine)算法将源代码的字符序列分割成一系列符号(Token)

记号类型
array标识符
[左方括号
index标识符
]右方括号
=赋值
(左圆括号
index标识符
+加号
4数字
)右圆括号
*乘号
(左圆括号
2数字
+加号
6数字
)右圆括号

词法分析产生的符号分为以下几类:关键字、标识符、字面量(包含数字、字符串等)和特殊符号(如加号、等号)。

与此同时,扫描器也完成了将标识符放入符号表,将数字、字符串常量放入文字表等工作,以备后续步骤使用。

语法分析

语法分析器(Grammar Parser)对扫描器生成的符号进行语法分析,整个过程采取上下文无关语法(Context-free Grammar)分析法,生成语法树,就是以表达式(Expression)为结点的树。

语义分析

接下来就是语义分析器(Semantic Analyzer)对表达式进行语义层面的分析。

编译器所能分析的语义叫做静态语义(Static Semantic),也就是在编译期可以确定的语义,与之对应的叫动态语义(Dynamic Semantic),要到运行期才能确定的语义。

静态语义包括声明和类型的匹配,类型的转换。比如浮点型表达式赋值给整型表达式,这里就隐含了类型转换工作。

动态语义一般是运行期出现的相关问题,比如将 0 作为除数就是一个运行期语义错误。

中间语言生成

源代码优化器(Source Code Optimizer)会将整个语法树转换成中间代码(Intermediate Code)。比如 (2 + 6)就会转换成 8。

中间代码使得编译器可以被分为前端和后端,前端负责产生和机器无关的中间代码,后端将中间代码转换为目标机器代码。这样一些跨平台编译器可以共用一套前端,针对不同平台生成不同后端即可。

目标代码生成与优化

代码生成器(Code Generator)将中间代码转化为目标机器代码。 然后目标代码优化器(Target Code Generator)对目标代码进行优化,比如选择合适的寻址方式。

经过这么多步骤编译器最终将源代码编译成未链接的目标文件。如图:

静态链接

链接的主要过程包括地址与空间分配(Address and Storage Allocation)符号决议(Symbol Resolution)重定位(Relocation)等步骤。

最基本的静态链接过程,每个模块的源代码文件经过编译器的编译,形成目标文件(Object File,.o 或 .obj),目标文件和库(Library)一起链接形成最终的可执行文件。

比如目标文件 A 中一个变量 Foo 再链接了目标文件 B 之后才能确定地址。确定后,链接器就要对这个地址进行修正,这个修正的过程就叫重定位(Relocation),每个要被修正的地方叫一个重定位入口(Relocation Entry)

总结

本篇简述了源代码到可执行文件的 4 个步骤:预编译、编译、汇编和链接。

其中详细介绍了编译过程中的:词法分析、语法分析、语义分析、中间代码生成和目标代码的生成与优化。

最后介绍了一些链接过程中的基本概念:重定位、目标文件、库等。

经过刚才的梳理,可以回答开头的问题了吗,如果还不能,或者想了解更多,欢迎阅读原书。如果发现了我的错误与纰漏,也请不吝赐教,十分感谢。

该博客文章由作者通过 CC BY 4.0 进行授权。

程序员的自我修养 ——链接、装载与库读书笔记(一)

程序员的自我修养 ——链接、装载与库读书笔记(三)