• 【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;
    }
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    第5讲:SQL语句之DML类型的数据操纵语言
    饿了么三面:让你怀疑人生的Spring Boot夺命连环40问
    Swagger2 介绍与集成
    C++之<set和map模拟实现>
    tomcat 端口 8005 被 windows 系统服务占用导致启动闪退的问题
    [附源码]java毕业设计石林县石漠化信息查询分析系统
    【蓝桥杯Web】第十四届蓝桥杯(Web 应用开发)模拟赛 2 期 | 精品题解
    给 hugo 博客添加搜索功能
    企业如何寻找可替代serv-u的国产文件传输系统?
    sklearn基础篇(五)-- 线性模型
  • 原文地址:https://blog.csdn.net/weixin_45651194/article/details/133782683