目录
一、程序的翻译环境和执行环境
二、编译链接过程
2.1、程序编译过程
2.2、程序编译链接的阶段
2.2.1、预处理
2.2.2、编译
2.2.3、汇编
2.2.4、链接
2.2.5、整体过程
三、运行环境
一、程序的翻译环境和执行环境
在ANSI C的任何一种实现中,存在两个不同的环境
第一种是翻译环境(由编译器完成),在这个环境中源代码转换为可执行的机器指令。
第二种是执行环境,它用于执行代码
二、编译链接过程
2.1、程序编译过程
一个源文件要经过编译,链接才能形成可执行程序。因此每个源文件经过编译器处理生成目标文件,多个生成的目标文件和链接库经过连接器处理生成可执行程序。
在vs2019下编译器是cl.exe,链接器是link.exe
连接器同时也会引入标准C函数库中任何被该程序所用到的函数
2.2、程序编译链接的阶段
2.2.1、预处理
1、进行头文件的包含,将头文件的相关内容包含进test.i文件中。(#include预处理指令)
在Linux环境下,头文件放在/usr/include目录下。
2、define定义符号的替换(#define预处理指令)
3、去注释
[hx@VM-24-7-centos 20240120-编译链接]$ vim test.c
1 #include<stdio.h>
2
3 extern int Add(int,int);
4
5 //定义MAX的值
6 #define MAX 1000
7
8 int main()
9 {
10 int x=MAX;
11 int a=10;
12 int b=20;
13 int c=Add(a,b);
14 printf("%d\n",c);
15
16 return 0;
17 }
[hx@VM-24-7-centos 20240120-编译链接]$ gcc add.c -E -o add.i
[hx@VM-24-7-centos 20240120-编译链接]$ gcc test.c -E -o test.i
[hx@VM-24-7-centos 20240120-编译链接]$ ls
add.c add.i test.c test.i
### -E选项,让预编译后停下来
### -o选项 编译结束后把结果放到test.i文件中
2.2.2、编译
把C语言代码翻译成汇编代码,进行词法分析,语法分析,语义分析,符号汇总
符号汇总一般是全局符号,全局变量,局部变量不会汇总进来(局部变量只能在函数内部使用)
本程序中,全局符号是Add,main
[hx@VM-24-7-centos 20240120-编译链接]$ gcc add.i -S
[hx@VM-24-7-centos 20240120-编译链接]$ gcc test.i -S
[hx@VM-24-7-centos 20240120-编译链接]$ ls
add.c add.i add.s test.c test.i test.s
[hx@VM-24-7-centos 20240120-编译链接]$ vim test.s
### -S选项 编译结束后让程序停下来,结果保存在test.s中,不再往后走
2.2.3、汇编
符号汇总,形成符号表,并且将汇编指令转换成二进制指令最后生成test.o文件,汇编完成生成的目标文件经过连接器处理生成可执行程序。
[hx@VM-24-7-centos 20240120-编译链接]$ gcc add.i -c
[hx@VM-24-7-centos 20240120-编译链接]$ gcc test.i -c
[hx@VM-24-7-centos 20240120-编译链接]$ ls
add.c add.i add.o add.s test.c test.i test.o test.s
### -c选项 汇编完成之后就停下来,结果保存在test.o中
2.2.4、链接
合并段表,合并符号表以及符号表的重定位(解决找到外部符号问题)
符号表的合并和重定位,当最终生成可执行程序时,符号表也需要合并,main函数有确定的地址,Add出现了两个符号,要合并时需要重定位,但是知道test.o文件中Add的地址不是有效地址,是状态值,没有用处,但是add.o文件中符号表里的Add是有效地址,符号名一样就合并符号
两个符号表合并就可以通过地址找到函数,解决的是如何跨文件找外部符号问题,可以链接其它文件的函数,当然外部符号没有定义的时候,就会出现链接报错。
在Linux环境下 test.o的可执行程序的格式是.elf,通过readelf工具可以看懂elf文件
通过加选项-s来查看test.o,add.o形成的符号表。
add.o test.o文件,按照elf文件格式组织的时候把文件分成各种各样的段,把相同段上的内容合并在一起,成为合并段表
[hx@VM-24-7-centos 20240120-编译链接]$ readelf test.o
Usage: readelf <option(s)> elf-file(s)
Display information about the contents of ELF format files
Options are:
-a --all Equivalent to: -h -l -S -s -r -d -V -A -I
-h --file-header Display the ELF file header
-l --program-headers Display the program headers
--segments An alias for --program-headers
-S --section-headers Display the sections' header
--sections An alias for --section-headers
-g --section-groups Display the section groups
-t --section-details Display the section details
-e --headers Equivalent to: -h -l -S
-s --syms Display the symbol table
--symbols An alias for --syms
[hx@VM-24-7-centos 20240120-编译链接]$ readelf test.o -s
Symbol table '.symtab' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 74 FUNC GLOBAL DEFAULT 1 main
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND Add
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
[hx@VM-24-7-centos 20240120-编译链接]$ readelf add.o -s
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS add.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
8: 0000000000000000 20 FUNC GLOBAL DEFAULT 1 Add
2.2.5、整体过程
源文件要通过编译链接形成可执行程序,其中编译阶段就包括预处理,编译,汇编阶段生成目标源文件,链接是通过链接器将链接库和多个目标源文件链接在一起生成可执行程序,其中编译阶段的预处理过程是头文件包含,宏替换,去注释,编译阶段将C语言代码通过语法分析,词法分析,语义分析,符号汇总,最后生成汇编代码,汇编阶段就是将汇编代码翻译成二进制指令,生成符号表。链接阶段进行合并段表以及合并符号表和符号表的重定位。
因此源文件经过编译链接,汇总符号,形成符号表,链接期间会把跨多个文件的符号进行链接,如果链接时有符号没有有效地址,就会有链接错误报错