揭秘编译和链接--C++
时间 2020-12-21
标签
C++
程序员的自我修养
《程序员的自我修养--链接、装载与库》学习笔记
对应平常的应用开发,我们很少关注编译和链接过程,这些事都被IDE替我们完成了。那我们写的代码,是怎么组织起来的?最后是怎么运行起来的呢?今天跟大家一起来学习下。
代码编写完成到最后变成可执行的exe,中间需要四个步骤:预编译、编译、汇编和链接。
预编译
定义:由源文件“.cpp/.c”生成“.i”文件,这是在预编译阶段完成的;gcc -E .cpp/.c --->.i
功能:
- 展开所有的宏定义,消除“#define”;
- 处理所有的预编译指令,比如#if、#ifdef等;
- 处理#include预编译指令,将包含文件插入到该预编译的位置;
- 删除所有的注释“/**/”、"//"等;
- 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及错误提醒;
- 保留所有的#program编译指令,原因是编译器要使用它们;
编译
定义:由“.i”文件生成“.s”文件,生成汇编代码文件,这是在编译阶段完成的;gcc -S .i --->.s
功能:
- 词法分析:将源代码文件的字符序列划分为一系列的记号,一般词法分析产生的记号有:标识符、关键字、数字、字符串、特殊符号(加号、等号);在识别记号的同时也将标识符放好符号表、将数字、字符放入到文字表等;有一个lex程序可以实现词法扫描,会按照之前定义好的词法规则将输入的字符串分割成记号,所以编译器不需要独立的词法扫描器;
- 语法分析:语法分析器将对产生的记号进行语法分析,产生语法树----就是以表达式尾节点的树,一步步判断如何执行表达式操作。下图为一个语法树
- 语义分析:由语法阶段完成分析的并没有赋予表达式或者其他实际的意义,比如乘法、加法、减法,必须经过语义阶段才能赋予其真正的意义;
- 字符集映射
- 逻辑翻译
- 优化后生成相应的汇编代码文件
- 汇总所有符号
汇编
定义:由“.s”文件生成的“.obj”文件,生成可重定位的二进制文件;gcc -c .s-->.o;
功能:
- 此文件中生成符号表,能够产生符号的有:所有数据都要产生符号、指令只产生一个符号(函数名);
- 一个cpp生成一个obj
- 数据段、代码段
- 导出符号表
- 未解决符号表
- 地址重定向表
链接
定义:链接得到库(dll)或可执行文件(exe)
功能:
- 合并所有“.obj”文件的段
- 调整段偏移和段长度(按照段的属性合并,属性可以是“可读可写”、“只读”、“可读可执行”,合并后将相同属性的组织在一个页面内,比较节省空间)
- 合并符号表,进行符号解析完成后给符号分配地址;其中符号解析的意思是:所有.obj符号表中对符号引用的地方都要找到该符号定义的地方。在编译阶段,有数据的地方都是0地址,有函数的额地方都是下一行指令的偏移量-4(由于指针是4字节);
- 可执行文件以页面对齐。
- 符号的重定位(链接核心):将符号分配的虚拟地址写回原先未分配正确地址的地方
编译和链接图示