事实上,我们平时所谓的编程只是一堆代码,真正的程序是在我们经过编译器处理后得到的那个可执行的文件。代码变成程序要依次经过以下处理:
我们写一个简短的代码来演示该过程
1、预处理:生成.i文件 操作命令gcc -E -o main.i main,c
(1)删除#define、宏替换、#if、#endif处理
(2)递归展开头文件,删除注释、添加行号和文本标识。
(3)保留所用的#program编译器指令,因为编译器需要使用它们
2.编译:生成.s文件 操作命令gcc -S -o main.s main.i
(1)词法分析,语法分析,语义分析
(2)代码优化;
(3)生成指令,外部符号放在未定义区域,连接时根据强弱符号规则进行处理
3.汇编:生成.o文件 操作命令gcc -c -o main.o main.s
(1)翻译指令,生成可重入的二进制目标文件(机器指令),此时符号的地址和偏移仍然是虚拟地址
查看该ELF文件如上图,section name中的各个属性只有文件大小和文件位置有值,其余的VMA虚拟内存地址和 LMA逻辑地址 都是0值,也就是说没有地址,自然这些段中数据是无法访问的。
4.链接:生成.exe文件 操作命令gcc -o main main.s
(1)对符号表和段进行合并
(2)符号解析:在该符号的引用处找到它的定义处(只处理全局符号global)
(3)分配地址空间
(4)符号的重定位:对错误指令的修正
生成的.exe文件
现在我们就发现有了确切的虚拟内存地址和逻辑地址,说明完成了到虚拟内存的映射。
连接的一系列操作完成后有了程序的入口地址,就可以进行接下来的运行操作
5.运行: ./main ./生成的可执行文件名
(1)完成可执行文件到虚拟内存的映射,虚拟内存到物理内存的映射,创建页表、页目录。将程序的入口地址写到pc寄存器中。
(2)加在指令和数据到内存中(以页的方式加载,一页4k大小)。
(3)获取环境变量、命令行参数