LV02-03-编译与调试-01-GCC基础
本文主要是编译与调试——GCC基础的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
说明:此篇笔记若有更新,系统环境可能会发生变化,但大概的应该都是一样的。
PC端开发环境 | Windows | Windows11 |
Ubuntu | Ubuntu20.04.6的64位版本 | |
VMware® Workstation 17 Pro | 17.0.0 build-20800274 | |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) | |
Win32DiskImager | Win32DiskImager v1.0 | |
Linux开发板环境 | Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) | |
linux内核 | linux-4.15(NXP官方提供) |
点击查看本文参考资料
- 通用
参考资料 | 相关链接 |
--- | --- |
点击查看相关文件下载
--- | --- |
一、 GCC 简介
1. GNU?
我们先来看一下GNU,官网是这么说的:
GNU 是一个 自由软件 操作系统—就是说,它尊重其使用者的自由。GNU 操作系统包括 GNU 软件包(专门由 GNU 工程发布的程序)和由第三方发布的自由软件。GNU 的开发使你能够使用电脑而无需安装可能会侵害你自由的软件。
GNU 是一个类 Unix 操作系统。它是由多个应用程序、系统库、开发工具乃至游戏构成的程序集合。GNU 的开发始于 1984 年 1 月,称为 GNU 工程。GNU 的许多程序在 GNU 工程下发布;我们称之为 GNU 软件包。
- 自由软件运动
自由软件运动致力于通过自由软件使计算机用户获得自由计算的权利。自由软件的用户可以自主控制自己的计算。非自由软件使用户受制于软件开发者。现在自由软件运动远远不仅是开发 GNU 系统。
- 自由软件意味着使用者有运行、复制、发布、研究、修改和改进该软件的自由。
自由软件关乎自由,而非价格。要理解这个概念,你应该考虑 “free” 是 “言论自由(free speech)”中的“自由”;而不是 “免费啤酒(free beer)”中的“免费”。
更精确地说,自由软件赋予软件使用者四项基本自由:
(1)不论目的为何,有运行该软件的自由(自由之零)。
(2)有研究该软件如何工作以及按需改写该软件的自由(自由之一)。取得该软件源代码为达成此目的之前提。
(3)有重新发布拷贝的自由,这样你可以借此来敦亲睦邻(自由之二)。
(4)有向公众发布改进版软件的自由(自由之三),这样整个社群都可因此受惠。取得该软件源码为达成此目的之前提。
2. GCC编译工具链
GCC编译工具链(toolchain)是指以GCC编译器为核心的一整套工具,用于把源代码转化成可执行应用程序。它主要包含以下三部分内容:
- gcc-core:即GCC编译器,用于完成预处理和编译过程,例如把C代码转换成汇编代码。
- Binutils :除GCC编译器外的一系列小工具包括了链接器ld,汇编器as、目标文件格式查看器readelf等。
- glibc:包含了主要的 C语言标准函数库,C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。
在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。
3. GCC编译器是什么?
GCC 全称为 GNU CC , GNU 项目中符合 ANSI C 标准的编译系统 ,可以编译如 C 、 C++ 、 Object C 、 Java 、 Fortran 、 Pascal 、 Modula-3 和 Ada 等多种语言。
它是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高 20%~30% 。它是一个交叉平台编译器 ,适合在嵌入式领域进行开发编译。GCC 还能运行在不同的操作系统上,如 Linux 、 Solaris 、 Windows 等。
GCC 支持的文件后缀名解释如下:
.c | C原始程序 |
.C/.cc/.cxx | C++原始程序 |
.h | 预处理文件(头文件) |
.i | 已经过预处理的C原始程序 |
.ii | 已经过预处理的C++原始程序 |
.s/.S | 汇编语言原始程序 |
.o | 目标文件 |
.a/.so | 编译后的库文件 |
.m | Objective-C源代码文件 |
4. GCC版本查看
在Ubuntu系统下系统默认已经安装好GCC编译器(若是没安装,也可以直接使用apt-get等相关命令安装),可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径:
- gcc –version
1 | sumu@sumu-virtual-machine:~$ gcc --version |
- gcc -v
1 | sumu@sumu-virtual-machine:~$ gcc -v |
这个命令的显示信息更详细:
(1)“Target: x86_64-linux-gnu”表示该GCC的目标平台为x86_64架构(Intel、AMD的CPU), 表示它编译生成的应用程序只适用于x86架构,不适用于ARM开发板平台。
(2)“gcc version 9.4.0”表明该GCC的版本为9.4.0,部分程序可能会对编译器版本有要求, 像编译指定版本的uboot、Linux内核的时候可能会对GCC有版本要求。
5. GCC 安装位置
- which gcc
1 | sumu@sumu-virtual-machine:~$ which gcc |
6. Binutils工具集
6.1 工具简介
Binutils(bin utility),是GNU二进制工具集,通常跟GCC编译器一起打包安装到系统,它的官方说明网站地址为:Binutils - GNU Project - Free Software Foundation
在进行程序开发的时候通常不会直接调用这些工具,而是在使用GCC编译指令的时候由GCC编译器间接调用。下面是其中一些常用的工具:
- as:汇编器,把汇编语言代码转换为机器码(目标文件)。
- ld:链接器,把编译生成的多个目标文件组织成最终的可执行程序文件。
- readelf:可用于查看目标文件或可执行程序文件的信息。
- nm : 可用于查看目标文件中出现的符号。
- objcopy: 可用于目标文件格式转换,如.bin 转换成 .elf 、.elf 转换成 .bin等。
- objdump:可用于查看目标文件的信息,最主要的作用是反汇编。
- size:可用于查看目标文件不同部分的尺寸和总尺寸,例如代码段大小、数据段大小、使用的静态内存、总大小等。
- addr2line:将地址转换为文件名和行号。这个命令还是很有用的,经常可以用于段错误的排查。
6.2 这些工具都在哪?
系统默认的Binutils工具集位于/usr/bin目录下,可使用如下命令查看系统中存在的Binutils工具集:
1 | sumu@sumu-virtual-machine:~$ ls /usr/bin/ | grep linux-gnu- |
7. glibc库
7.1 库简介
glibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库,因为绝大部分C程序都依赖该函数库,该文件甚至会直接影响到系统的正常运行,例如常用的文件操作函数read、write、open,打印函数printf、动态内存申请函数malloc等。
在Ubuntu系统下,libc.so.6是glibc的库文件,可直接执行该库文件查看版本,在主机上执行如下命令:
1 | sumu@sumu-virtual-machine:~$ /lib/x86_64-linux-gnu/libc.so.6 |
表示本系统中使用的glibc是2.31版本,是由GCC 9.4.0版本的编译器编译出来的。
7.2 源码
学习C语言的时候,可能特别好奇printf、malloc之类的函数是如何实现的, 但是在Windows下的C库是不开源的,无法查看,而在Linux下, 则可以直接研究glibc的源代码,甚至加入开发社区贡献自己的代码,glibc的官网地址为: The GNU C Library - GNU Project - Free Software Foundation,可在该网站中下载源代码来学习。
二、编译器
了解了那么多GCC的相关基础知识,那么什么是编译器呢?
1. 什么是编译器
编译器,作为计算机科学与软件工程的核心工具,将人类可读的高级语言源代码转换成机器可执行的低级指令。这一复杂的过程涉及多个阶段,包括词法分析、语法分析、语义分析、优化和代码生成等。
2. 编译器由哪些组成?
一个编译器主要的组成部分如下所示:
分析器 | 分析器将源语言程序代码转换为汇编语言。因为要从一种格式转换为另一种格式(C到汇编),所以分析器需要知道目标机器的汇编语言。 |
汇编器 | 汇编器将汇编语言代码转换为CPU可以执行字节码。 |
链接器 | 链接器将汇编器生成的单独的目标文件组合成可执行的应用程序。链接器需要知道这种目标格式以便工作。 |
标准C库 | 核心的C函数都有一个主要的C库来提供。如果在应用程序中用到了C库中的函数,这个库就会通过链接器和源代码连接来生成最终的可执行程序。 |
编译器主要由前端和后端两大部分组成。前端负责处理源代码,包括词法分析、语法分析和语义分析,生成中间表示(IR)。后端则根据中间表示生成目标代码,包括优化和代码生成等步骤。
4. 编译器的核心技术
(1)词法分析与正则表达式:编译器使用正则表达式或有限自动机进行词法分析,识别源代码中的不同成分。
(2)语法分析与解析算法:编译器通常采用递归下降解析或基于上下文的无关文法(CFG)进行语法分析,构建AST(抽象语法树)。
抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使合个阶段变得混乱。因些,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。
抽象语法树在很多领域有广泛的应用,比如浏览器,智能编辑器,编译器。
(3)语义分析与类型系统:编译器通过检查类型、作用域、符号表等来确保源代码的语义正确性。
(4)中间表示与优化技术:编译器使用SSA(静态单一赋值)形式、控制流图等中间表示,并采用各种优化技术如常量折叠、死代码消除、循环展开等来提高代码质量。
(5)目标代码生成与机器模型:编译器根据目标机器的指令集和特性生成高效的机器代码。
参考资料: