《程序员的自我修养》学习笔记——揭秘源文件到可执行文件的编译过程【第一弹】

程序代码到可执行程序编译链接过程

预编译

以c++/c 语言为例,预编译阶段的工作有以下几点:

  1. 处理所有#define 及条件预编译指令(如 #if,#ifdef.....),并展开所有宏定义。
  2. 删除所有注释("//" ,"/**/")。
  3. 处理 "#include",将被包含文件插入该预编译指令位置。(整过过程递归进行,因为被包含文件也可能包含其他文件)
  4. 添加行号与文件标识。(用于调试时产生的编译错误及报错等信息)
预编译过程相当于如下命令:

gcc -E hello.c -o hello.i  (-E 表示只进行预编译)
或者
cpp hello.c > hello.i
编译

编译过程可以分为如下步骤:

image

  1. 扫描

  2. 词法分析

    ​ 运用一种类似于有限状态机的算法,将源代码的字符序列分割为一系列记号(关键字、标识符、字面常量、特殊符号等)。【一个名叫lex的程序可以完成这项任务】

  3. 语法分析

    ​ 对由扫描器产生的记号进行语法分析,进而产生语法树。(采用上下文无关的语法分析手段)【同样一个叫做yacc的工具也可完成这项任务】

  4. 语义分析

    ​ 包括静态语义(如声明和类型的匹配、类型的转化等)和动态语义(运行阶段才能确定)。

  5. 源代码优化【这阶段也包括中间代码(例如llvm 中的 IR)的生成】

    ​ 由于直接在语法树上作优化难度较大,源代码优化器通常将语法树转化为中间代码,再进行优化。

  6. 目标代码生成和目标代码优化

    ​ 代码生成器将中间代码转化成目标机器代码。

    ​ 接着目标代码优化器对上述目标代码进行优化。(如选择合适的寻址方式,删除多余指令等)

编译过程相当于如下命令:
gcc -S hello.i -o hello.s (.s 是汇编输出文件的后缀)
或者
gcc -S hello.c -o hello.s  (预编译和编译合并了)

汇编

汇编器将汇编代码转变为机器可以执行的指令。(生成可重定位文件 .o)

编译过程相当于如下命令:
as  hello.s -o hello.o 
或者
gcc -c hello.s -o hello.o 
或者
gcc -c hello.c -hello.o (上面三个过程一步完成)

链接

对于一个复杂的软件,将每个源代码模块独立地翻译,然后组装。这个组装模块的过程就是链接。(主要包括地址和空间分配、符号决议、重定位等步骤)

最基本的静态链接过程:每个模块的源代码文件(如.c)文件经过编译器编译成可重定位文件(Object File,扩展名为.o或.obj),可重定位文件和库一起链接形成最终可执行文件(.out)。

image

链接过程相当于如下命令:

gcc  hello.o -o hello.out 
以如下代码为例:

#include<stdio.h>

int main()
{
printf("hello world");
return 0;
}
预编译(hello.i) 编译(hello.s)
image image
汇编(hello.o) 链接(hello.out)
image image

可重定位文件 [.o 或 .obj]

可重定位文件的格式

目前PC平台流行的可执行文件格式(Executable)主要是:

PE(Windows)和 ELF(Linux)。【两者都发源自 COFF 可执行文件格式】

另外的如ios 是 Mach-O格式android 是dex格式。

而可重定位文件是源代码编译后但未进行链接的中间文件。(Windows 下的.obj 和 Linux 下的.o)。

因此,可重定位文件和可执行文件的内容和结构是很相似的。(可以广义的将二者看作一种类型的文件)

同时动态链接库(Windows 下的.dll 和 Linux 下的.so)和 静态链接库(Windows 下的.lib 和 Linux 下的.a)文件都可按照可执行文件格式存储。

【小技巧: Linux 下可使用file命令查看相应的文件格式】

程序的指令和数据分开存放的好处:

  1. 程序装载后,数据和指令分别映射到两个虚存区域。数据区域对进程而言是可读写的,指令区域对于进程而言是只读的。这样可以防止程序指令被有意或者无意地更改。
  2. 利于提高程序的局部性。(提高缓存的命中率)
  3. 当系统中运行着多个该程序副本时,内存中只需要保存一份该程序的指令部分。(最重要的原因)

原文链接:https://www.cnblogs.com/Only-xiaoxiao/p/17174043.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:《程序员的自我修养》学习笔记——揭秘源文件到可执行文件的编译过程【第一弹】 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • C++实现截图截屏的示例代码

    下面是“C++实现截图截屏的示例代码”的详细攻略: 一、使用Windows API Windows API提供了一系列函数来实现截图截屏的功能。其中,最常用的是BitBlt函数。以下是示例代码: #include <Windows.h> #include <iostream> int main() { // 获取屏幕DC HDC hd…

    C 2023年5月23日
    00
  • oppor1c配置怎么样?价格多少?

    Oppo R1C的配置和价格详解 Oppo R1C的配置 Oppo R1C是一款在2015年初推出的定位中高端的手机,其主要配置包括: 处理器:骁龙615(64位八核); 存储:2G RAM + 16GB ROM,支持最高128GB外部存储卡; 屏幕:5英寸1080P全高清; 摄像头:后置1300万像素,前置500万像素; 电池:2420mAh(不可拆卸);…

    C 2023年5月23日
    00
  • C++ Boost Pool超详细讲解

    C++ Boost Pool超详细讲解 概述 C++ Boost库中的Pool库是一个有用的内存分配和管理库。它是一个头文件库,可以在C++程序中使用,使内存管理变得更加高效和简单。它的目标是在没有垃圾收集器的情况下提高内存分配的效率。 Pool库提供了两个主要的类来支持内存池: boost::pool: 这个类定义了一个通用内存池,可以用于管理任意大小的对…

    C 2023年5月23日
    00
  • C语言十进制转二进制代码实例

    下面是关于“C语言十进制转二进制代码实例”的完整攻略。 1. 基本思路 将一个十进制数转换成二进制数,可以采用“除2取余法”实现。具体步骤如下: 用十进制数除以2,获取商和余数; 将余数存储下来; 将商作为新的除数,重复执行上述过程,直到商为0为止; 将所有余数按逆序排列,即可得到二进制数。 比如将“26”转换成二进制数,具体操作如下: 26 ÷ 2 = 1…

    C 2023年5月30日
    00
  • C++实现调用系统时间简单示例

    下面我将为你详细讲解“C++实现调用系统时间简单示例”的完整攻略。 1. 环境要求 在开始示例代码的实现之前,我们需要确保本地环境已包含C++编译器。可以选择在本地安装VS Code或者其他的编译器软件。以下是某些流行的编译器: Visual Studio CodeBlocks Dev-C++ 在这个示例过程中,我们将使用VS Code作为开发环境。 2. …

    C 2023年5月23日
    00
  • C++ 内存分配处理函数set_new_handler的使用

    当C++程序在运行时发现内存分配失败时,会抛出一个std::bad_alloc异常。为了避免程序崩溃,我们可以使用set_new_handler函数来注册一个新的处理函数,当内存分配失败时,程序会调用该函数来处理内存分配失败的情况。 set_new_handler函数的语法 set_new_handler函数是一个全局函数,它的原型如下: std::new_…

    C 2023年5月23日
    00
  • 浅析shellcode 反汇编模拟运行及调试方法

    让我来介绍一下关于“浅析shellcode 反汇编模拟运行及调试方法”的完整攻略。 1. 背景介绍 首先,让我们来了解一下什么是shellcode。shellcode指的是一个用于利用计算机系统的漏洞或开发后门的一段二进制代码。一般情况下,这个shellcode由黑客手动编写,并通过某个手段传送到受害机器上去执行。为了成功执行shellcode,黑客们通常会…

    C 2023年5月22日
    00
  • 荣耀畅玩8C手机怎么样?荣耀畅玩8C全面评测

    荣耀畅玩8C手机怎么样?荣耀畅玩8C全面评测 前言 荣耀畅玩8C是一款2018年10月上市的入门级智能手机。作为荣耀畅玩系列产品的一员,荣耀畅玩8C主打高性价比,具有充足的配置和不错的性能表现。在这篇文章中,我们将对荣耀畅玩8C进行全面评测,从外观、配置、性能以及其他方面对其进行详细剖析。 外观设计 荣耀畅玩8C采用了6.26英寸的水滴屏,分辨率为1520x…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部