系统编程学习笔记第一章:预备篇

      大家好,我是小智!

程序的概念

      一般的讲,程序是一系列有序指令的集合,目的是告诉计算机如何完成某些指定的操作或者如何解决某个问题。

      计算机早期程序采用机器语言设计,但是由于开发效率较低,所以后面设计出汇编语言,但是由于汇编语言的汇编指令较多,并且不同的公司及组织对研发平台设计出不同的汇编指令集,而这些汇编指令集并不兼容,所以为了进一步提高开发效率和降低开发难度,就设计出高级语言,比如面向过程编程的C语言,和面向对象编程的C++以及其他编程语言。

image

      开发人员根据编程语言的语法和规则设计出程序,程序的格式和类型一般由使用的编程语言决定,比如采用C语言设计的程序就是以xxx.c作为后缀名,采用Python设计的程序就是以xxx.py作为后缀名。

      程序只是开发人员为了实现某些需求设计出来的文本,是存储在计算机的磁盘中的,想要得到程序的运行结果,是需要使用编译器等工具对程序进行编译等一系列操作,目的是把程序转换为可执行文件,可执行文件内部其实就是对应平台可以识别的指令和数据的集合,可执行文件得到运行就是计算机的中央处理器CPU对可执行文件内部的指令进行处理,对可执行文件内部的数据进行运算的过程。

image

      想要了解程序中指令和数据具体的执行流程,需要熟悉计算机组成原理以及汇编语言,其中计算机组成部分一般遵循冯诺依曼结构,也就是由控制器运算器存储器输入设备输出设备五个部分组成。

image

程序的编译

      一般在编写出程序之后,并不能直接运行,而是需要把程序通过编译器进行编译,生成可执行文件才能运行。而对于嵌入式学习,一般都是在Linux系统下使用GCC编译器对程序进行编译。

      GCC编译器是Linux系统默认的C/C++编译器,大部分Linux发行版本中都是默认安装的。GCC编译器主要以Linux命令的形式在shell终端中使用,所以需要大家掌握关于GCC编译器的相关参数。

C语言程序编译过程:源程序 ---- 预处理 --- 编译 --- 汇编 --- 链接 --- 可执行文件

预处理

      对源码进行简单的加工,GCC编译器会调用预处理器cpp对程序进行预处理,其实就是解释源程序中所有的预处理指令,如#include(文件包含)、#define(宏定义)、#if(条件编译)等以#号开头的预处理语句。

      这些预处理指令将会在预处理阶段被解释掉,如会把被包含的文件拷贝进来,覆盖掉原来的#include语句,把所有的宏定义展开,所有的条件编译语句被执行,GCC还会把所有的注释删掉,添加必要的调试信息。

预处理指令: gcc -E xxx.c -o xxx.i 会生成预处理文件 xxx.i

编译

      就是对经过预处理之后的.i文件进行进一步翻译,也就是对语法、词法的分析,最终生成对应硬件平台的汇编文件,具体生成什么平台的汇编文件取决于编译器,比如X86平台使用gcc编译器,而ARM平台使用交叉编译工具arm-linux-gcc。

编译指令:gcc -S xxx.i -o xxx.s 会生成对应汇编文件 xxx.s

汇编

      GCC编译器会调用汇编器as将汇编文件翻译成可重定位文件,其实就是把.s文件的汇编代码翻译为相应的指令。

编译指令:gcc -c xxx.s -o xxx.o 会生成汇编后的文件 xxx.o

链接

      经过汇编步骤后生成的.o文件其实是ELF格式的可重定位文件,虽然已经生成了指令流,但是需要重定位函数地址等,所以需要链接系统提供的标准C库和其他的gcc基本库文件等,并且还要把其他的.o文件一起进行链接。-lc -lgcc 是默认的,可以省略

编译指令:gcc hello.o -o hello -lc -lgcc 会得到可执行文件 xxx // l是lib的缩写

程序的格式

      一般对源文件进行编译和汇编之后,就会得到对应的目标文件xxx.o,目标文件也被称为可重定位文件,就是指还未完成链接的中间文件,用户可以把这些目标文件制作成在Linux系统下使用的静态库libxxx.a或者动态库libxxx.so。

      其实目标文件和可执行文件的内容和结构非常相似,所以Linux系统下把目标文件xxx.o和可执行文件都称为ELF文件,ELF指的是Executable Linkable Format的缩写,中文翻译为可执行可链接格式。

注意:可以在linux系统中利用查看文件格式的指令: file 查阅目标文件和可执行文件的区别:

image

思考

  • 还未完成链接的目标文件中存储了哪些内容,以及如何查看目标文件中的内容信息???

      回答:目标文件中的内容至少有编译后的机器指令代码、数据。除了这些内容以外,目标文件中还包括了链接时所需要的一些信息,比如符号表、调试信息、字符串等。一般目标文件将这些信息按不同的属性,以“节”(Section)的形式存储,有时候也叫“段”(Segment),一般情况下,它们都表示一个一定长度的区域,基本上不加以区别。

反汇编

      程序源代码编译后的机器指令经常被放在代码段(Code Section)里,代码段常见的名字有“.code”或“.text”。全局变量和局部静态变量数据经常放在数据段(Data Section),数据段一般名字都叫“.data”。

注意:linux系统的GCC编译套件有一款工具叫做objdump,可以查看目标文件内部的数据!

image

      数据段中有一个叫做.bss段, bss以前其实是汇编伪指令,作用是为某些数据预留一块空间,bss其实是Block Started by Symbol,也就是用于存储未被初始化的全局变量和静态局部变量,但是在程序编译阶段是没有分配空间的,在程序运行时会得到空间。

image

常用的段名 说明
.rodatal 存放只读数据,比如字符串常量、全局 const 变量(与 .rodata 相同)。
.comment 存放编译器版本信息,例如 "GCC:(GNU)4.2.0"
.debug 存放调试信息。
.dynamic 存放动态链接信息。
.hash 符号哈希表。
.line 存放调试时的行号表,即源代码行号与编译后指令的对应关系。
.note 存放额外的编译器信息,比如程序公司名、发布版本号等。
.strtab String Table,字符串表,用于存储 ELF 文件中用到的各种字符串。
.symtab Symbol Table,符号表。
.shstrtab Section String Table,段名表,存储段名称的字符串表。
.plt 动态链接的跳转表和全局入口表(Procedure Linkage Table)。
.init 程序初始化与终结代码段。

另外,objdump工具也可以实现把可执行文件进行反汇编,可以把得到的反汇编代码重定向到某个文本中进行查看 “objdump -D demo > > xxx.txt”

posted @ 2025-06-17 16:17  努力赚钱的小智  阅读(102)  评论(0)    收藏  举报