【System】系统编程基础:编译和链接

参考:《程序员的自我修养:链接、装载和库》Chapter 2

一、从源代码到可执行文件

学习任何语言都是先写 Hello World,但它从源代码到输出结果中间发生了太多事情,但我们一般会笼统地说一句 “编译器将源代码编译成更底层的机器语言”。但对于进行系统编程的人来说这是远远不够的,我们必须拨开这层迷雾,仔细瞅瞅里面的门道。

从源代码到输出大致可以分为四个步骤:预处理、编译、汇编和链接

1.1 预处理

在学习 C 语言都会学到预处理器指令(或预编译指令),如 #define#if#pragma 等。它们就是在预处理阶段被处理的指令,常见处理规则如下:

  • 删除所有的 #define,展开所有宏定义;
  • 处理所有条件预编译指令,如 #if#elif#endif 等;
  • 递归处理 #include 指令,插入相应文件到预编译指令位置上;
  • 删除所有注释;
  • 添加文件名和行号标识;
  • 保留所有的 #pragma 编译器指令,它用于指导编译器活动。

1.2 编译和汇编

编译阶段的处理参考编译原理,按顺序进行词法分析、语法分析、语义分析、源码优化、目标代码生成和目标代码优化。

词法分析是将源码切分为 Token,类型有标识符、常数、操作符等,在 Unix 系统中可以用 lex 程序实现。
语法分析通过上下文无关文法对 Token 构建语法树,在 Unix 系统中可以使用 yacc 工具。
语义分析进行静态语义分析,包括声明和类型的匹配、类型的转换,完成后语法树上被标识了类型。
源码优化若在语法树上操作通常较难,因此需要将其转为一种中间表示(IR),常见的有三地址码和P-代码。
目标代码生成和目标代码优化在后端处理。

引入中间表示后,可以将编译器分为前端和后端,前端处理与目标机器无关的中间表示,后端将中间表示转为目标代码。

编译的产物是汇编语言程序,汇编阶段再将汇编语言程序转为机器语言程序,即 .o 文件,它是二进制文件。

经过上述复杂活动后,我们将源代码文件编译成了目标代码文件,但该文件存在一个问题,目标代码中的某些变量定义在了其他模块,我们如何确定它们的地址呢?此时就需要引入链接。

1.3 链接

链接分为动态链接和静态链接,这里先讨论静态链接。链接其实就是模块间的通信问题,最常见的就是(1)模块间的函数调用;(2)模块间的变量访问。静态链接就像拼图,把不同的模块组装起来,形成一个完整的画面(功能)。

链接过程主要是两个步骤:

  1. 地址与空间分配;
  2. 符号解析与重定向。

其中重点是符号解析与重定向。以下面代码为例,foo 是一个外部函数,在处理时会先将 foo 函数地址置为 0,在链接时查找重定向表中 foo 的地址,再修改 0 地址为查到的地址。

int main() {
	foo();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值